using System.Security.Cryptography; namespace KronaStaffTerminal.Services; public static class AdminPinService { public const int CurrentIterations = 600_000; public static (string Salt, string Hash, int Iterations) Hash(string pin) { var salt = RandomNumberGenerator.GetBytes(16); var hash = Rfc2898DeriveBytes.Pbkdf2(pin, salt, CurrentIterations, HashAlgorithmName.SHA256, 32); return (Convert.ToBase64String(salt), Convert.ToBase64String(hash), CurrentIterations); } public static bool Verify(string pin, string saltBase64, string hashBase64, int iterations) { if (string.IsNullOrWhiteSpace(pin) || string.IsNullOrWhiteSpace(saltBase64) || string.IsNullOrWhiteSpace(hashBase64)) { return false; } try { var salt = Convert.FromBase64String(saltBase64); var expected = Convert.FromBase64String(hashBase64); var actual = Rfc2898DeriveBytes.Pbkdf2( pin, salt, Math.Clamp(iterations, 120_000, CurrentIterations), HashAlgorithmName.SHA256, expected.Length); return CryptographicOperations.FixedTimeEquals(actual, expected); } catch (FormatException) { return false; } } }