Skip to content

Password Strength Analyser — C# source

Estimate password strength with zxcvbn - realistic dictionary and pattern cracking with crack-time estimates and improvement suggestions. Runs entirely in your browser.

This is the C# implementation — the same logic the interactive tool runs, in a shareable, citable form.

// password-strength-analyser — C# port: password strength scoring & feedback. (.NET 7+). Port of src/lib/password-strength.ts — same self-contained heuristic estimator as this dir's python.py (the live TS/Go twins wrap zxcvbn, whose ranking data is too large to vendor).
using System;
using System.Collections.Generic;
using System.Linq;

namespace PasswordStrengthAnalyser;

/// <summary>Strength breakdown — mirrors the TS PasswordAnalysis / Go pwstrength.Result.</summary>
public readonly record struct Analysis(int Score, string Label, double Entropy, string CrackTime);

public static class PasswordStrength
{
    static readonly string[] Labels = { "Very weak", "Weak", "Fair", "Good", "Strong" };
    static readonly double[] Thresholds = { 28, 36, 60, 128 };
    /// <summary>Curated common-password blocklist — an exact (lowercased) match forces score 0.</summary>
    static readonly HashSet<string> Common = new() {
        "1234", "12345", "123123", "123456", "12345678", "123456789", "1234567890",
        "000000", "111111", "654321", "666666", "abc123", "admin", "baseball", "batman",
        "dragon", "iloveyou", "letmein", "login", "master", "monkey", "passw0rd", "password",
        "password1", "princess", "qwerty", "root", "shadow", "superman", "sunshine", "trustno1", "welcome" };
    static readonly string[] Sequences = { "qwertyuiop", "asdfghjkl", "zxcvbnm", "1234567890", "0987654321", "abcdefg", "gfedcba" };
    /// <summary>Guess-pool size: 26 lower + 26 upper + 10 digit + 33 ASCII symbol + 128 other.</summary>
    static int PoolSize(string pw)
    {
        bool lower = false, upper = false, digit = false, sym = false, other = false;
        foreach (char c in pw)
            if (c >= 128) other = true; else if (char.IsLower(c)) lower = true;
            else if (char.IsUpper(c)) upper = true; else if (char.IsDigit(c)) digit = true; else sym = true;
        return (lower ? 26 : 0) + (upper ? 26 : 0) + (digit ? 10 : 0) + (sym ? 33 : 0) + (other ? 128 : 0);
    }
    /// <summary>Does <paramref name="lower"/> contain a length-4 window of <paramref name="seq"/>?</summary>
    static bool ContainsSequence(string lower, string seq) =>
        seq.Length >= 4 && Enumerable.Range(0, seq.Length - 3).Any(i => lower.Contains(seq[i..(i + 4)]));
    /// <summary>Human crack time at 1e10 guesses/s — zxcvbn's offline-fast display.</summary>
    static string CrackTime(double entropy)
    {
        if (entropy <= 0 || Math.Pow(2, entropy) / 1e10 < 1) return "instant";
        double seconds = Math.Pow(2, entropy) / 1e10;
        (double div, string name) = seconds < 60 ? (1, "second") : seconds < 3_600 ? (60, "minute")
            : seconds < 86_400 ? (3_600, "hour") : seconds < 2_592_000 ? (86_400, "day")
            : seconds < 31_536_000 ? (2_592_000, "month") : (31_536_000, "year");
        double v = seconds / div;
        return v >= 1e9 ? "centuries" : v >= 1e6 ? $"{v / 1e6:F0} million years"
            : v >= 1e3 ? $"{v / 1e3:F0} thousand years" : $"{v:F0} {name}s";
    }

    /// <summary>Scores 0-4. <paramref name="userInputs"/> seeds the estimator with
    /// related tokens (username, site name) — a substring match weakens the score.</summary>
    public static Analysis Analyse(string password, params string[] userInputs)
    {
        int len = password.Length; int pool = PoolSize(password);
        double lp = pool > 1 ? Math.Log2(pool) : 0; int unique = password.Distinct().Count();
        // Base entropy len*log2(pool), capped by variety — repeats earn quarter credit.
        double entropy = pool > 1 ? Math.Min(len * lp, (unique + (len - unique) * 0.25) * lp) : 0;
        string lower = password.ToLowerInvariant();
        if (Common.Contains(lower)) entropy = 0; // blocklist hit — dictionary collapse
        else
        {
            foreach (string seq in Sequences) if (ContainsSequence(lower, seq)) entropy -= 12;
            foreach (string ui in userInputs)
                if (ui.Length >= 3 && lower.Contains(ui.ToLowerInvariant())) entropy -= 10;
            entropy = Math.Max(0, entropy);
        }
        int score = len < 4 || entropy < Thresholds[0] ? 0 : entropy < Thresholds[1] ? 1
            : entropy < Thresholds[2] ? 2 : entropy < Thresholds[3] ? 3 : 4;
        return new Analysis(score, Labels[score], entropy, CrackTime(entropy));
    }
}

public class Demo
{
    public static void Main()
    {
        foreach (string pw in new[] { "password", "12345678", "Tr0ub4dour&3", "correct horse battery staple", "u2#9Xq!Lp$7wZ" })
        {
            Analysis r = PasswordStrength.Analyse(pw);
            Console.WriteLine($"{pw,-26} {r.Score}/4 {r.Label,-9} {r.Entropy,6:F1} bits  {r.CrackTime}");
        }
    }
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →