using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public static class BoardUtility
{
// Константы из Python скрипта
private const uint POLY_A = 0x54505353;
private const uint POLY_B = 0x504F5450;
private const uint POLY_C = 0x53534F54;
private const uint POLY_SEED = 0x5450534F;
private const byte XOR_KEY = 0xD7;
private const uint SERIAL_LENGTH = 9;
private const uint ENCRYPTED_LENGTH = 38;
private const uint GARBAGE_LENGTH = 12;
private const string CHARSET = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ";
private const string ENCODE_TABLE = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+$";
///
/// Расшифровка обфусцированного серийника (38 символов)
///
public static string DecryptSerialObfuscated(string obfuscatedData)
{
if (string.IsNullOrEmpty(obfuscatedData))
throw new ArgumentException("Input data cannot be null or empty");
if (obfuscatedData.Length != ENCRYPTED_LENGTH)
throw new ArgumentException($"Invalid length: expected {ENCRYPTED_LENGTH}, got {obfuscatedData.Length}");
try
{
// Извлекаем закодированные данные, пропуская мусор
StringBuilder encoded = new StringBuilder();
// Пропускаем первые 4 символа мусора (0-3)
// Берем символы 4-12 (9 символов данных)
encoded.Append(obfuscatedData.Substring(4, 9));
// Пропускаем символы 13-16 (4 символа мусора)
// Берем символы 17-25 (9 символов данных)
encoded.Append(obfuscatedData.Substring(17, 9));
// Декодируем (18 символов -> 9 байт)
byte[] encrypted = new byte[9];
string encodedStr = encoded.ToString();
for (int i = 0; i < 18; i += 2)
{
byte decodedByte = DecodeFromBase64Like(encodedStr[i], encodedStr[i + 1]);
encrypted[i / 2] = decodedByte;
}
// Расшифровываем XOR
StringBuilder decrypted = new StringBuilder();
byte prev = 0x3C;
for (int i = 0; i < encrypted.Length; i++)
{
byte decryptedChar = (byte)(encrypted[i] ^ XOR_KEY ^ (i * 17) ^ prev);
decrypted.Append((char)decryptedChar);
prev = encrypted[i];
}
return decrypted.ToString();
}
catch (Exception ex)
{
throw new Exception($"Obfuscated decryption error: {ex.Message}", ex);
}
}
///
/// Расшифровка HEX серийника (старый формат)
///
public static string DecryptSerialHex(string encryptedHex)
{
if (string.IsNullOrEmpty(encryptedHex))
throw new ArgumentException("Input data cannot be null or empty");
try
{
byte[] encryptedBytes = HexStringToByteArray(encryptedHex);
StringBuilder decrypted = new StringBuilder();
byte prev = 0x3C;
for (int i = 0; i < encryptedBytes.Length; i++)
{
byte decryptedChar = (byte)(encryptedBytes[i] ^ XOR_KEY ^ (i * 17) ^ prev);
decrypted.Append((char)decryptedChar);
prev = encryptedBytes[i];
}
return decrypted.ToString();
}
catch (Exception ex)
{
throw new Exception($"HEX decryption error: {ex.Message}", ex);
}
}
///
/// Универсальный метод расшифровки - автоматически определяет формат
///
public static string DecryptSerial(string encryptedData)
{
if (string.IsNullOrEmpty(encryptedData))
throw new ArgumentException("Input data cannot be null or empty");
// Определяем формат по длине
if (encryptedData.Length == ENCRYPTED_LENGTH) // Обфусцированный формат
{
return DecryptSerialObfuscated(encryptedData);
}
else if (encryptedData.Length == SERIAL_LENGTH * 2) // HEX формат
{
return DecryptSerialHex(encryptedData);
}
else // Предполагаем, что это уже открытый серийник
{
return encryptedData;
}
}
///
/// Декодирование из base64-подобного формата
///
private static byte DecodeFromBase64Like(char c1, char c2)
{
int val1 = ENCODE_TABLE.IndexOf(c1);
int val2 = ENCODE_TABLE.IndexOf(c2);
if (val1 == -1) val1 = 0;
if (val2 == -1) val2 = 0;
return (byte)(((val1 << 2) & 0xFC) | ((val2 >> 4) & 0x03));
}
///
/// Преобразование HEX строки в массив байт
///
private static byte[] HexStringToByteArray(string hex)
{
if (hex.Length % 2 != 0)
throw new ArgumentException("Hex string must have even length");
byte[] bytes = new byte[hex.Length / 2];
for (int i = 0; i < bytes.Length; i++)
{
string byteValue = hex.Substring(i * 2, 2);
bytes[i] = Convert.ToByte(byteValue, 16);
}
return bytes;
}
///
/// Генерация ожидаемого серийника из MAC адреса (для проверки)
///
public static string GenerateSerialFromMac(string macAddress)
{
if (string.IsNullOrEmpty(macAddress))
throw new ArgumentException("MAC address cannot be null or empty");
// Преобразуем MAC в число
string cleanMac = macAddress.Replace(":", "").Replace("-", "");
ulong macNumber = ulong.Parse(cleanMac, System.Globalization.NumberStyles.HexNumber);
ulong macLow = macNumber & 0xFFFFFFFF;
ulong macHigh = (macNumber >> 32) & 0xFFFF;
ulong x = macLow ^ macHigh;
// Полином с обрезкой до 64 бит
ulong result = ((ulong)POLY_A * x * x * x + (ulong)POLY_B * x * x + (ulong)POLY_C * x + (ulong)POLY_SEED) & 0xFFFFFFFFFFFFFFFF;
char[] serial = new char[SERIAL_LENGTH];
for (int i = 0; i < SERIAL_LENGTH; i++)
{
int index = (int)((result >> (i * 5)) % (ulong)CHARSET.Length);
serial[i] = CHARSET[index];
result = ((result * (ulong)POLY_A + (ulong)POLY_B) ^ (result >> 13)) & 0xFFFFFFFFFFFFFFFF;
}
return new string(serial);
}
///
/// Проверка валидности серийника для MAC адреса
///
public static bool VerifySerial(string macAddress, string serialOrEncrypted)
{
try
{
string decryptedSerial = DecryptSerial(serialOrEncrypted);
string expectedSerial = GenerateSerialFromMac(macAddress);
return decryptedSerial == expectedSerial;
}
catch
{
return false;
}
}
///
/// Проверка валидности серийника для MAC адреса с возвратом подробной информации
///
public static (bool IsValid, string Message) VerifySerialDetailed(string macAddress, string serialOrEncrypted)
{
try
{
string decryptedSerial = DecryptSerial(serialOrEncrypted);
string expectedSerial = GenerateSerialFromMac(macAddress);
if (decryptedSerial == expectedSerial)
{
return (true, "Valid serial");
}
else
{
return (false, $"Invalid! Expected: {expectedSerial}, Got: {decryptedSerial}");
}
}
catch (Exception ex)
{
return (false, $"Verification error: {ex.Message}");
}
}
}