Masterly.Hash 2.0.0

dotnet add package Masterly.Hash --version 2.0.0
                    
NuGet\Install-Package Masterly.Hash -Version 2.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Masterly.Hash" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Masterly.Hash" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Masterly.Hash" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Masterly.Hash --version 2.0.0
                    
#r "nuget: Masterly.Hash, 2.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Masterly.Hash@2.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Masterly.Hash&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Masterly.Hash&version=2.0.0
                    
Install as a Cake Tool

Masterly.Hash

Masterly.Hash: password hashing you can't get wrong.

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 make Verify allocate 4 GiB or run with t=1.
  • Upgrade on login. Hashes from an older profile, an older pepper key, or Masterly.Hash 1.x verify and come back with Rehash set.
  • 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.
  • SensitiveString for request models: ToString(), the debugger and JSON output all show ***.
  • Metrics via System.Diagnostics.Metrics (meter Masterly.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.
  • HmacPepper keeps 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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • 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.

Version Downloads Last Updated
2.0.0 215 11/3/2026
1.0.0 508 1/12/2022