Masterly.Hash
2.0.0
dotnet add package Masterly.Hash --version 2.0.0
NuGet\Install-Package Masterly.Hash -Version 2.0.0
<PackageReference Include="Masterly.Hash" Version="2.0.0" />
<PackageVersion Include="Masterly.Hash" Version="2.0.0" />
<PackageReference Include="Masterly.Hash" />
paket add Masterly.Hash --version 2.0.0
#r "nuget: Masterly.Hash, 2.0.0"
#:package Masterly.Hash@2.0.0
#addin nuget:?package=Masterly.Hash&version=2.0.0
#tool nuget:?package=Masterly.Hash&version=2.0.0
Masterly.Hash

Password hashing you can't get wrong. Two methods, safe defaults, and existing hashes upgrade themselves on login.
dotnet add package Masterly.Hash
using Masterly.Hash;
string stored = Password.Hash("hunter2"); // $mh$p=2026a$<salt>$<tag>
var result = Password.Verify("hunter2", stored);
if (result.IsMatch && result.Rehash is { } fresh) // only when the stored hash is outdated
user.PasswordHash = fresh;
What you get
- Argon2id (RFC 9106) by default, PBKDF2-HMAC-SHA512 for NIST-only environments.
- Dated profiles, not knobs. The stored string carries a profile id (
2026a), never cost parameters. Verification looks the id up in a compiled-in allowlist, so no database row can makeVerifyallocate 4 GiB or run witht=1. - Upgrade on login. Hashes from an older profile, an older pepper key, or Masterly.Hash 1.x verify and come back with
Rehashset. - Constant-time verification, secrets zeroed after use, NFKC normalization (NIST SP 800-63B), 1024-byte input cap. Empty passwords and invalid UTF-16 are rejected rather than silently mangled.
- User-enumeration protection:
VerifyOrDummy(password, storedHashOrNull)costs the same whether or not the user exists. - Login-flood protection: a concurrency limit (default: processor count) so parallel Argon2 calls can't exhaust memory; excess calls wait, then throw
PasswordHasherBusyException(map it to 503). - Peppers with key rotation (
HmacPepper, or Vault/KMS/Key Vault providers in separate packages). - Onion wrapping:
TryWrap(legacyHash)upgrades weak hashes offline, without the password. SensitiveStringfor request models:ToString(), the debugger and JSON output all show***.- Metrics via
System.Diagnostics.Metrics(meterMasterly.Hash) for OpenTelemetry.
Profiles
| Profile | Id | Algorithm | Parameters | For |
|---|---|---|---|---|
HashProfile.Server2026a (default) |
2026a |
Argon2id | m=64 MiB, t=3, p=1 | Web backends |
HashProfile.Constrained2026a |
2026c |
Argon2id | m=19 MiB, t=2, p=1 | Serverless, small containers |
HashProfile.Fips2026a |
2026f |
PBKDF2-HMAC-SHA512 | 210,000 iterations | NIST-approved primitives only |
A profile id is permanent. Stronger parameters ship as a new id in a new package version; existing hashes keep verifying and are rehashed on the next login.
Configuration
var hasher = new PasswordHasher(new PasswordHasherOptions
{
DefaultProfile = HashProfile.Constrained2026a,
Pepper = HmacPepper.FromBase64("k2", new Dictionary<string, string>
{
["k1"] = config["Pepper:k1"], // old key: still verifies, triggers rehash
["k2"] = config["Pepper:k2"], // current key
}),
MaxConcurrency = 8,
});
Register it as a singleton (or use Masterly.Hash.DependencyInjection).
Upgrading from 1.x
1.x hashes (base64(SHA-2(password || salt) || salt)) are accepted automatically. You don't need to know which SHA-2 variant produced them. The first successful login returns Rehash. The old Hash/IHash API still compiles but is marked [Obsolete]; its Verify is now constant-time and no longer throws when given the wrong algorithm.
To protect dormant accounts now, wrap their hashes offline. Wrapping needs the algorithm, which 1.x defaulted to SHA-512:
var options = new PasswordHasherOptions();
options.LegacyFormats[0] = new V1LegacyFormat(V1Algorithm.SHA512);
var hasher = new PasswordHasher(options);
if (hasher.TryWrap(user.PasswordHash, out string wrapped)) user.PasswordHash = wrapped;
See the migration guide.
Security notes
- Verifying a legacy hash is much faster than Argon2, so response timing can show that an account still has a legacy hash. Migrate quickly, or wrap legacy hashes offline.
HmacPepperkeeps keys in process memory. Keep them out of the database; otherwise a database leak includes the pepper.- The profile allowlist guards against resource exhaustion and misconfiguration. It does not protect against an attacker who can already write to your users table.
Full documentation: https://github.com/a7mdfre7at/Masterly.Hash
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- No dependencies.
NuGet packages (14)
Showing the top 5 NuGet packages that depend on Masterly.Hash:
| Package | Downloads |
|---|---|
|
Masterly.Hash.Legacy
Universal hash migrator for Masterly.Hash: verify bcrypt, ASP.NET Core Identity (v2/v3), Django PBKDF2, phpass (WordPress/phpBB), unsalted MD5/SHA digests and external Argon2 PHC hashes, upgrade them to Argon2id on login, onion-wrap weak hashes offline, and audit a password table. |
|
|
Masterly.Hash.Integrity
Tamper evidence for files and records: streaming content hashes (SHA-256/384/512, HMAC-SHA256), RFC 9162 Merkle trees with inclusion and consistency proofs, keyed hash-chain audit logs, and directory manifests that report added, removed and modified files. |
|
|
Masterly.Hash.Pepper.AzureKeyVault
Azure Key Vault pepper for Masterly.Hash: loads pepper keys from Key Vault secrets (each secret version is a key id), caches them in memory with periodic refresh, and computes HMAC-SHA256 locally. |
|
|
Masterly.Hash.Testing
Test helpers for code that uses Masterly.Hash: a real but microsecond-fast PasswordHasher, a call-recording decorator, a deterministic fake pepper, framework-agnostic assertions and known sample hashes. For test projects only; never reference it from production code. |
|
|
Masterly.Hash.DependencyInjection
Microsoft.Extensions.DependencyInjection integration for Masterly.Hash: AddMasterlyHash() registers a singleton IPasswordHasher, binds options from IConfiguration (profiles, limits, pepper key ring), validates them at startup and exposes a builder for peppers, legacy formats and profiles. |
GitHub repositories
This package is not used by any popular GitHub repositories.