chip-gameV3/Assets/Scripts/Input/ComSystem/BoardAuthentication.cs

117 lines
3.6 KiB
C#

using System;
using System.Security.Cryptography;
using System.Text;
public static class BoardAuthentication
{
private const int _blockSizeBytes = 16;
private const int _hexBlockLength = _blockSizeBytes * 2;
private static readonly byte[] _aesObfuscatedKey =
{
0x69, 0x40, 0xC7, 0x0E, 0xF2, 0xC7, 0x45, 0x70,
0x72, 0xCD, 0x28, 0x58, 0x99, 0x8F, 0x73, 0xA8,
0x5F, 0x5F, 0xED, 0xB4, 0xC1, 0x62, 0xB4, 0x24,
0xAC, 0xD7, 0xA3, 0xD6, 0xD8, 0x0B, 0x50, 0x37
};
public static string CreateChallengeHex()
{
byte[] challenge = new byte[_blockSizeBytes];
using RandomNumberGenerator generator = RandomNumberGenerator.Create();
generator.GetBytes(challenge);
return ToHex(challenge);
}
public static string EncryptHexBlock(string plainHex)
{
byte[] plain = ParseHexBlock(plainHex);
using Aes aes = Aes.Create();
aes.KeySize = 256;
aes.BlockSize = 128;
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.None;
aes.Key = (byte[])_aesObfuscatedKey.Clone();
using ICryptoTransform encryptor = aes.CreateEncryptor();
byte[] encrypted = encryptor.TransformFinalBlock(plain, 0, plain.Length);
return ToHex(encrypted);
}
public static bool TryExtractHexPayload(string line, string prefix, out string payload)
{
payload = null;
if (string.IsNullOrEmpty(line)
|| string.IsNullOrEmpty(prefix)
|| line.Length != prefix.Length + _hexBlockLength
|| !line.StartsWith(prefix, StringComparison.Ordinal))
return false;
string candidate = line.Substring(prefix.Length);
for (int i = 0; i < candidate.Length; i++)
{
if (!IsHexCharacter(candidate[i]))
return false;
}
payload = candidate.ToUpperInvariant();
return true;
}
public static bool ValidateEncryptedResponse(string challengeHex, string responseHex)
{
if (!TryNormalizeHexBlock(responseHex, out string normalizedResponse))
return false;
string expectedResponse = EncryptHexBlock(challengeHex);
int difference = 0;
for (int i = 0; i < expectedResponse.Length; i++)
difference |= expectedResponse[i] ^ normalizedResponse[i];
return difference == 0;
}
private static byte[] ParseHexBlock(string value)
{
if (!TryNormalizeHexBlock(value, out string normalized))
throw new ArgumentException($"Ожидался HEX-блок длиной {_hexBlockLength} символа.", nameof(value));
byte[] bytes = new byte[_blockSizeBytes];
for (int i = 0; i < bytes.Length; i++)
bytes[i] = Convert.ToByte(normalized.Substring(i * 2, 2), 16);
return bytes;
}
private static bool TryNormalizeHexBlock(string value, out string normalized)
{
normalized = null;
if (string.IsNullOrEmpty(value) || value.Length != _hexBlockLength)
return false;
for (int i = 0; i < value.Length; i++)
{
if (!IsHexCharacter(value[i]))
return false;
}
normalized = value.ToUpperInvariant();
return true;
}
private static bool IsHexCharacter(char value)
=> value >= '0' && value <= '9'
|| value >= 'A' && value <= 'F'
|| value >= 'a' && value <= 'f';
private static string ToHex(byte[] bytes)
{
StringBuilder builder = new(bytes.Length * 2);
for (int i = 0; i < bytes.Length; i++)
builder.Append(bytes[i].ToString("X2"));
return builder.ToString();
}
}