I hereby claim:
- I am edamtoft on github.
- I am edamtoft (https://keybase.io/edamtoft) on keybase.
- I have a public key ASAW4K_70VpLoPo8TegJ_4-8BBhrH2FxGKoSBEO0cqVYogo
To claim this, I am signing this object:
[TypeConverter(typeof(Converter))] | |
public readonly struct DomainObject : IEquatable<DomainObject>, IEquatable<string> | |
{ | |
public readonly string _value; | |
public DomainObject(string value) => _value = value ?? throw new ArgumentNullException(nameof(value)); | |
public bool Equals(DomainObject other) => StringComparer.OrdinalIgnoreCase.Equals(_value, other._value); | |
public bool Equals(string other) => StringComparer.OrdinalIgnoreCase.Equals(_value, other); |
public sealed class RateLimitingOptions | |
{ | |
public int Attempts { get; set; } | |
public int Seconds { get; set; } | |
public int RedisDb { get; set; } | |
} |
I hereby claim:
To claim this, I am signing this object:
public interface IRateLimiter | |
{ | |
Task<T> RunRateLimited<T>(Func<Task<T>> func); | |
} |
public interface IPasswordHasher | |
{ | |
string Hash(string password); | |
(bool Verified, bool NeedsUpgrade) Check(string hash, string password); | |
} |
public sealed class HashingOptions | |
{ | |
public int Iterations { get; set; } = 10000; | |
} |
public sealed class PasswordHasher : IPasswordHasher | |
{ | |
private const int SaltSize = 16; // 128 bit | |
private const int KeySize = 32; // 256 bit | |
public PasswordHasher(IOptions<HashingOptions> options) | |
{ | |
Options = options.Value; | |
} |
public string Hash(string password) | |
{ | |
using (var algorithm = new Rfc2898DeriveBytes( | |
password, | |
SaltSize, | |
Options.Iterations, | |
HashAlgorithmName.SHA256)) | |
{ | |
var key = Convert.ToBase64String(algorithm.GetBytes(KeySize)); | |
var salt = Convert.ToBase64String(algorithm.Salt); |
public (bool Verified, bool NeedsUpgrade) Check(string hash, string password) | |
{ | |
var parts = hash.Split('.', 3); | |
if (parts.Length != 3) | |
{ | |
throw new FormatException("Unexpected hash format. " + | |
"Should be formatted as `{iterations}.{salt}.{hash}`"); | |
} |
public sealed class PasswordHasher : IPasswordHasher | |
{ | |
private const int SaltSize = 16; // 128 bit | |
private const int KeySize = 32; // 256 bit | |
public PasswordHasher(IOptions<HashingOptions> options) | |
{ | |
Options = options.Value; | |
} |