Developers

.NET usage

Package ForgeHash 1.0.0-experimental. The class shares the namespace name — alias it in call sites. Part of the developers path.

Experimental software. Do not store production passwords with ForgeHash.

Install

From nuget.org (experimental prerelease):

dotnet add package ForgeHash --prerelease
dotnet add package ForgeHashX --prerelease
dotnet tool install -g ForgeHash.Cli --prerelease
forgeh --help

ForgeHash · ForgeHashX · ForgeHash.Cli

Or pack / reference from this repo while developing:

dotnet pack src/ForgeHash.Core/ForgeHash.Core.csproj -c Release -o artifacts/nuget
dotnet add package ForgeHash --source ./artifacts/nuget --prerelease
dotnet add reference ../path/to/ForgeHash/src/ForgeHash.Core/ForgeHash.Core.csproj

See Developers for all registries and CLI examples. Publisher ops: docs/PUBLISHING.md.

Minimal login flow

using ForgeHash;
using ForgeHashApi = ForgeHash.ForgeHash; // class shares the namespace name

string encoded = ForgeHashApi.HashPassword(passwordBytes, ForgeHashParameters.Interactive);
bool ok = ForgeHashApi.VerifyPassword(passwordBytes, encoded);

if (ok && ForgeHashApi.NeedsRehash(encoded, ForgeHashParameters.Interactive))
{
    string upgraded = ForgeHashApi.HashPassword(passwordBytes, ForgeHashParameters.Interactive);
    // Persist upgraded hash
}

Profiles

ProfileWhen to use
ForgeHashParameters.DevelopmentLocal tests / CI only
ForgeHashParameters.InteractiveDefault research interactive cost
ForgeHashParameters.SensitiveHigher-cost experiments

Never silently default to Development in production-shaped builds.

Pepper (optional)

byte[] pepper = /* 32 bytes from a secrets manager */;
string encoded = ForgeHashApi.HashPassword(passwordBytes, pepper, parameters);
bool ok = ForgeHashApi.VerifyPassword(passwordBytes, pepper, encoded);

The pepper never appears in the encoded string.

Exact bytes vs strings

  • ReadOnlySpan<byte> APIs are exact.
  • string APIs encode UTF-8 and do not Unicode-normalize.
  • Immutable strings cannot be wiped; prefer byte buffers for secrets when practical.

Parsing

if (!ForgeHashParser.TryParse(encoded, out ParsedForgeHash? parsed) || parsed is null)
{
    // malformed stored hash
}
else
{
    // parsed.MemoryKiB / Iterations / Parallelism / Salt / Hash
}

VerifyPassword already returns false for malformed input.

Low-level research API

byte[] digest = ForgeHashApi.DeriveHash(password, salt, parameters);

This does not generate a salt. You must supply one.

Sample project

dotnet run --project samples/ForgeHash.Sample -- "demo-password"