using System.Security.Cryptography; using System.Text; namespace GameList.Infrastructure; public static class PasswordHasher { private const int SaltSize = 16; private const int KeySize = 32; private const int Iterations = 100_000; public static (byte[] Hash, byte[] Salt) HashPassword(string password) { if (string.IsNullOrEmpty(password)) throw new ArgumentException("Password required", nameof(password)); var salt = RandomNumberGenerator.GetBytes(SaltSize); var hash = PBKDF2(password, salt); return (hash, salt); } public static bool Verify(string password, byte[] hash, byte[] salt) { if (hash is null || salt is null || hash.Length == 0 || salt.Length == 0) return false; var computed = PBKDF2(password, salt); return CryptographicOperations.FixedTimeEquals(computed, hash); } private static byte[] PBKDF2(string password, byte[] salt) { return Rfc2898DeriveBytes.Pbkdf2( Encoding.UTF8.GetBytes(password), salt, Iterations, HashAlgorithmName.SHA256, KeySize); } }