using System.Security.Cryptography; using System.Text; namespace foodmarket.Application.Common; /// Хэширование PIN-кода кассира (PBKDF2-HMAC-SHA256 со случайной солью). /// PIN низкоэнтропийный (4–6 цифр), поэтому защита — хэш + серверный lockout /// (5 неверных → блок). Формат хранения: «base64(salt).base64(key)». public static class PinHasher { private const int SaltSize = 16; private const int KeySize = 32; private const int Iterations = 100_000; public static string Hash(string pin) { var salt = RandomNumberGenerator.GetBytes(SaltSize); var key = Rfc2898DeriveBytes.Pbkdf2(Encoding.UTF8.GetBytes(pin), salt, Iterations, HashAlgorithmName.SHA256, KeySize); return $"{Convert.ToBase64String(salt)}.{Convert.ToBase64String(key)}"; } public static bool Verify(string pin, string? stored) { if (string.IsNullOrEmpty(stored)) return false; var parts = stored.Split('.'); if (parts.Length != 2) return false; byte[] salt, key; try { salt = Convert.FromBase64String(parts[0]); key = Convert.FromBase64String(parts[1]); } catch { return false; } var test = Rfc2898DeriveBytes.Pbkdf2(Encoding.UTF8.GetBytes(pin), salt, Iterations, HashAlgorithmName.SHA256, key.Length); return CryptographicOperations.FixedTimeEquals(test, key); } /// PIN — 4–6 цифр. public static bool IsValidPin(string? pin) => pin is not null && pin.Length is >= 4 and <= 6 && pin.All(char.IsDigit); }