73 lines
No EOL
1.8 KiB
C#
73 lines
No EOL
1.8 KiB
C#
using System.Text;
|
|
using Geralt;
|
|
using JetBrains.Annotations;
|
|
|
|
namespace Femto.Modules.Auth.Models;
|
|
|
|
internal class UserPassword
|
|
{
|
|
private const int Iterations = 3;
|
|
private const int MemorySize = 67108864;
|
|
|
|
public Guid Id { get; private set; }
|
|
|
|
private byte[] Hash { get; set; }
|
|
|
|
private byte[] Salt { get; set; }
|
|
|
|
[UsedImplicitly]
|
|
private UserPassword() {}
|
|
|
|
public UserPassword(string password)
|
|
{
|
|
this.Id = Guid.NewGuid();
|
|
this.Salt = ComputeSalt();
|
|
this.Hash = ComputePasswordHash(password, Salt);
|
|
}
|
|
|
|
public bool Check(string password)
|
|
{
|
|
var matches = Argon2id.VerifyHash(
|
|
Hash,
|
|
Combine(password, Salt)
|
|
);
|
|
|
|
if (!matches)
|
|
return false;
|
|
|
|
if (Argon2id.NeedsRehash(Hash, Iterations, MemorySize))
|
|
{
|
|
this.Salt = ComputeSalt();
|
|
this.Hash = ComputePasswordHash(password, Salt);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static byte[] ComputeSalt() => System.Security.Cryptography.RandomNumberGenerator.GetBytes(32);
|
|
|
|
private static byte[] ComputePasswordHash(string password, byte[] salt)
|
|
{
|
|
var hash = new byte[128];
|
|
try
|
|
{
|
|
Argon2id.ComputeHash(hash, Combine(password, salt), Iterations, MemorySize);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw new SetPasswordError("Failed to hash password", e);
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
|
|
private static byte[] Combine(string password, byte[] salt)
|
|
{
|
|
var passwordBytes = Encoding.UTF8.GetBytes(password);
|
|
var hashInput = new byte[passwordBytes.Length + salt.Length];
|
|
passwordBytes.CopyTo(hashInput, 0);
|
|
salt.CopyTo(hashInput, passwordBytes.Length);
|
|
|
|
return hashInput;
|
|
}
|
|
} |