using Isopoh.Cryptography.Argon2;
using Isopoh.Cryptography.SecureArray;
using System.Security.Cryptography;
using System.Text;
namespace Hasher
{
///
/// A utility class for generating and verifying Argon2 hashes.
///
public static class Argon2dHasher
{
private static readonly RandomNumberGenerator Rng = RandomNumberGenerator.Create();
///
/// Generates an Argon2 hash for the given password and salt.
///
/// The password to hash.
/// A random salt to use for the hash. If null, a new 16-byte salt will be generated.
/// The generated Argon2 hash as a string.
public static string GenerateArgon2Hash(string password, byte[]? salt)
{
if (salt == null)
{
salt = new byte[16];
}
byte[] pwBytes = Encoding.UTF8.GetBytes(password);
// Generate a random salt
Rng.GetBytes(salt);
var config = new Argon2Config
{
Type = Argon2Type.DataIndependentAddressing,
Version = Argon2Version.Nineteen,
TimeCost = 6,
Lanes = 3,
Threads = Environment.ProcessorCount - 1, // higher than "Lanes" doesn't help (or hurt)
Password = pwBytes,
Salt = salt,
};
var argon2 = new Argon2(config);
string hashString;
using(SecureArray hashA = argon2.Hash())
{
hashString = config.EncodeString(hashA.Buffer);
}
return hashString;
}
///
/// Verifies that a given password matches an Argon2 hash.
///
/// The password to check.
/// The Argon2 hash to check against.
/// True if the password matches the hash, false otherwise.
public static bool VerifyArgon2Hash(string password, string hash)
{
byte[] pwBytes = Encoding.UTF8.GetBytes(password);
var valid = Argon2.Verify(hash, pwBytes);
return valid;
}
}
}