Zxcvbn 0.1.1

dotnet add package Zxcvbn --version 0.1.1
                    
NuGet\Install-Package Zxcvbn -Version 0.1.1
                    
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="Zxcvbn" Version="0.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Zxcvbn" Version="0.1.1" />
                    
Directory.Packages.props
<PackageReference Include="Zxcvbn" />
                    
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 Zxcvbn --version 0.1.1
                    
#r "nuget: Zxcvbn, 0.1.1"
                    
#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 Zxcvbn@0.1.1
                    
#: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=Zxcvbn&version=0.1.1
                    
Install as a Cake Addin
#tool nuget:?package=Zxcvbn&version=0.1.1
                    
Install as a Cake Tool

Zxcvbn

Realistic password strength estimation for .NET. A faithful, modern port of zxcvbn-ts (the maintained fork of Dropbox's zxcvbn).

Composition rules (require an uppercase letter, a digit and a symbol) are security theater. They reject correcthorsebatterystaple and wave through P@ssw0rd1, which is exactly backwards. Zxcvbn scores what attackers actually try: it matches a password against ranked frequency dictionaries, keyboard walks, repeats, sequences, dates and years, including reversed and l33t-substituted spellings, then runs the exact minimum-guess search to produce a single 0 to 4 score with honest crack-time estimates.

using Zxcvbn;

Result result = Core.EvaluatePassword("Tr0ub4dour&3");

Console.WriteLine(result.Score);                                   // 4
Console.WriteLine(result.GuessesLog10);                            // ~11.6
Console.WriteLine(result.CrackTimesDisplay.OfflineSlowHashing1e4PerSecond); // "1 year"
Console.WriteLine(result.CrackTimesDisplay.OnlineThrottling100PerHour);     // "centuries"
Console.WriteLine(result.Feedback.Warning);                        // null for a strong password

Why this package exists

zxcvbn is the de facto standard for password strength estimation. The JavaScript packages alone see millions of downloads a week, and there are ports in Go, Java, Rust and Python. On .NET the picture is worse: zxcvbn-core has not shipped since February 2021 (after 3.8M downloads, so the demand is real), and zxcvbn.net is stuck at 0.0.16. Neither tracks the improvements in the maintained zxcvbn-ts line. This package is a clean port of zxcvbn-ts 4.x.

NIST SP 800-63B recommends checking new passwords against lists of common and compromised values rather than imposing composition rules. Zxcvbn is the strength half of that recommendation.

Install

dotnet add package Zxcvbn

The package is a few megabytes because the ranked dictionaries are the product: common passwords, English words, first names, surnames, wikipedia terms, a diceware list and word sequences, plus six keyboard adjacency graphs, all embedded and compressed. A future Zxcvbn.Minimal without the bundled dictionaries is on the roadmap.

Score meaning

Score Meaning Guesses Guidance
0 Too guessable < 1e3 Risky. Reject.
1 Very guessable < 1e6 Protects only against throttled online attacks.
2 Somewhat guessable < 1e8 Protects against unthrottled online attacks.
3 Safely unguessable < 1e10 Moderate protection against offline slow-hash attacks.
4 Very unguessable >= 1e10 Strong protection against offline attacks.

The thresholds carry a small delta, exactly as zxcvbn does: the boundaries are 1e3 + 5, 1e6 + 5, 1e8 + 5 and 1e10 + 5 guesses, so a password sitting precisely on a power-of-ten lands in the lower score.

Each result also carries crack-time estimates for four scenarios: online with throttling (100 guesses/hour), online without throttling (10/second), offline with a slow hash such as bcrypt or argon2 (1e4/second) and offline with a fast hash (1e10/second), both as raw seconds and as human-readable strings.

Reuse the analyzer

Core.EvaluatePassword is a convenience wrapper over a shared default analyzer. To customise behavior, build a ZxcvbnAnalyzer once and reuse it. The analyzer is immutable and thread-safe, so a single instance can serve your whole application.

using Zxcvbn;

var options = new ZxcvbnOptions();
options.AddDictionary("company", new[] { "acmecorp", "acme", "roadrunner" });

var analyzer = new ZxcvbnAnalyzer(options);
Result result = analyzer.Evaluate("acmecorp2024");

User inputs

Passwords built from data an attacker can guess about a specific user (their name, email, username or your application and company names) should be penalised heavily. Pass those values as userInputs and they are treated as a per-evaluation dictionary.

using Zxcvbn;

var analyzer = new ZxcvbnAnalyzer();

// "Acme2024" scores 2 on its own, but only 1 once "Acme" is a known input.
Result withoutContext = analyzer.Evaluate("Acme2024");
Result withContext = analyzer.Evaluate("Acme2024", new[] { "Acme" });

ASP.NET Core Identity integration

The most common request is to gate registration on strength. Implement IPasswordValidator<TUser> and register it. This is the whole integration:

using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Zxcvbn;

public sealed class ZxcvbnPasswordValidator<TUser> : IPasswordValidator<TUser>
    where TUser : class
{
    private readonly ZxcvbnAnalyzer _analyzer = new();
    private readonly int _minimumScore;

    public ZxcvbnPasswordValidator(int minimumScore = 3) => _minimumScore = minimumScore;

    public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user, string? password)
    {
        if (password is null)
        {
            return Task.FromResult(IdentityResult.Failed(new IdentityError
            {
                Code = "PasswordRequired",
                Description = "A password is required.",
            }));
        }

        // Feed known user data in so passwords built from it are penalised.
        var userInputs = new List<string>();
        string? userName = manager.GetUserNameAsync(user).GetAwaiter().GetResult();
        if (!string.IsNullOrEmpty(userName))
        {
            userInputs.Add(userName);
        }

        Result result = _analyzer.Evaluate(password, userInputs);
        if (result.Score >= _minimumScore)
        {
            return Task.FromResult(IdentityResult.Success);
        }

        string message = result.Feedback.Warning ?? "This password is too easy to guess.";
        if (result.Feedback.Suggestions.Count > 0)
        {
            message += " " + string.Join(" ", result.Feedback.Suggestions);
        }

        return Task.FromResult(IdentityResult.Failed(new IdentityError
        {
            Code = "PasswordTooWeak",
            Description = message,
        }));
    }
}

Register it alongside the built-in validators:

builder.Services.AddIdentityCore<ApplicationUser>()
    .AddPasswordValidator<ZxcvbnPasswordValidator<ApplicationUser>>();

Inspecting the details

Every match that makes up the optimal cover is exposed on Result.Sequence, typed per pattern, so you can build a strength meter that explains itself.

using Zxcvbn;

Result result = Core.EvaluatePassword("qwerty12/12/2012");
foreach (Match match in result.Sequence)
{
    string extra = match switch
    {
        DictionaryMatch d => $"word '{d.MatchedWord}' from {d.DictionaryName}",
        SpatialMatch s => $"keyboard walk on {s.Graph}",
        DateMatch date => $"date {date.Year}-{date.Month}-{date.Day}",
        _ => match.Pattern,
    };
    Console.WriteLine($"{match.Token}: {extra}");
}

Notes and honesty

  • The bundled dictionaries are English and common-password oriented. Other languages will score less accurately until language packs are added (roadmap).
  • Package size comes from the embedded dictionaries. They are the product.
  • Guess estimates are computed with the same formulas as zxcvbn-ts. For ASCII and common Unicode this release has exact parity with zxcvbn-ts 4.1.2 as of reference year 2026: the score and the selected match sequence match exactly, and GuessesLog10 agrees to within IEEE double rounding (about 2e-15). This is validated by a development-time differential of over twelve thousand passwords against the reference, of which 1,508 are committed as fixtures. Tiny GuessesLog10 differences are possible where floating-point evaluation order differs.
  • The one exception to that parity is characters whose Unicode case-fold changes string length: İ (U+0130), the ff fi fl ffi ffl ſt st ligatures, ʼn (U+0149) and (U+1E9E). .NET's invariant case-fold keeps these length 1, while JavaScript expands them, which corrupts zxcvbn-ts's own internal dictionary index mapping. On the handful of contrived inputs that combine such a character with dictionary words the two implementations diverge, and the port may rate marginally stronger because it does not reproduce that reference behavior. Realistic single-character cases score at parity or more conservatively.
  • The reference year used by the date and recent-year heuristics is pinned per release (currently 2026) for reproducible results, whereas live zxcvbn-ts reads the current calendar year. They stay identical within the release year and drift by at most a year or two for year-bearing passwords after that. Each annual release bumps the pinned year and regenerates the fixtures; a build guard fails the tests if the pin ever falls more than a year behind.
  • Evaluation is fast for typical passwords (a few milliseconds), but a maximally adversarial input at the length cap can take up to about a second (still far faster than zxcvbn-ts). For attacker-controlled input, run Evaluate off the request thread; the 256-character cap bounds the worst case. A configurable work ceiling is on the roadmap.
  • This is a strength estimator, not a breach check. It does not tell you whether a specific password has appeared in a data breach. Pair it with a Have I Been Pwned k-anonymity lookup for that. A companion Zxcvbn.Pwned package is on the roadmap.

Roadmap

  • More language packs.
  • Zxcvbn.Minimal without the bundled dictionaries, for callers who supply their own.
  • Zxcvbn.Pwned, a Have I Been Pwned k-anonymity companion.

Credits

The algorithm and the dictionaries are the work of the zxcvbn and zxcvbn-ts authors. The algorithm was ported from @zxcvbn-ts/core; the dictionaries are sourced from @zxcvbn-ts/language-common and @zxcvbn-ts/language-en, and the original algorithm is Dropbox's zxcvbn. All are MIT licensed. Their full copyright notices are reproduced in THIRD-PARTY-NOTICES.md, which ships inside the NuGet package.

License

MIT. See LICENSE. This package also bundles MIT-licensed third-party data; see THIRD-PARTY-NOTICES.md.

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

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.1 75 8/7/2026
0.1.0 66 8/5/2026