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 — password strength scoring & feedback. Language: C (C11). Port of src/lib/password-strength.ts — same heuristic estimator as this dir's python.py (the live TS/Go twins wrap zxcvbn, too large to vendor); userInputs seeding omitted here for the 80-line budget (see java.java). */
#include <ctype.h>
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>

/* Strength labels indexed by score 0-4, + entropy thresholds (bits). */
static const char *LABELS[] = { "Very weak", "Weak", "Fair", "Good", "Strong" };
static const double THRESHOLDS[] = { 28, 36, 60, 128 };
/* Curated common-password blocklist — an exact (case-insensitive) match forces score 0. */
static const char *COMMON[] = { "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" };
/* Keyboard / numeric runs — any length-4 window inside costs 12 bits. */
static const char *SEQUENCES[] = { "qwertyuiop", "asdfghjkl", "zxcvbnm", "1234567890", "0987654321", "abcdefg", "gfedcba" };
typedef struct { int score; const char *label; double entropy; const char *crack_time; } Analysis;
/* Guess-pool size: 26 lower + 26 upper + 10 digit + 33 ASCII symbol + 128 other. */
static int pool_size(const char *pw)
{
    int cl = 0, cu = 0, cd = 0, cs = 0, co = 0;
    for (const unsigned char *p = (const unsigned char *)pw; *p; p++)
        if (*p >= 128) co = 1; else if (islower(*p)) cl = 1;
        else if (isupper(*p)) cu = 1; else if (isdigit(*p)) cd = 1; else cs = 1;
    return cl * 26 + cu * 26 + cd * 10 + cs * 33 + co * 128;
}
/* Human crack time at 1e10 guesses/s — zxcvbn's offline-fast display. */
static const char *crack_time(double e)
{
    static const double u[] = { 1, 60, 3600, 86400, 2592000, 31536000 };
    static const char *n[] = { "second", "minute", "hour", "day", "month", "year" };
    static char buf[32]; /* one buffer — fine for a single-threaded demo */
    double s = e > 0 ? pow(2, e) / 1e10 : 0, v; int i = 0;
    if (s < 1) return "instant";
    while (i < 5 && s >= u[i + 1]) i++;
    if ((v = s / u[i]) >= 1e9) return "centuries";
    if (v >= 1e6) snprintf(buf, sizeof buf, "%.0f million years", v / 1e6);
    else if (v >= 1e3) snprintf(buf, sizeof buf, "%.0f thousand years", v / 1e3);
    else snprintf(buf, sizeof buf, "%.0f %ss", v, n[i]);
    return buf;
}
/* Scores 0-4: pool entropy, variety cap, blocklist, sequence penalty. */
static Analysis analyse(const char *pw)
{
    size_t len = strlen(pw), unique = 0; int pool = pool_size(pw);
    double lp = pool > 1 ? log2((double)pool) : 0, raw = (double)len * lp;
    char lower[256]; bool seen[256] = { false }; /* lowercased copy + distinct-byte tally */
    for (size_t i = 0; i < len && i < sizeof lower - 1; i++) {
        unsigned char c = (unsigned char)pw[i];
        lower[i] = (char)tolower(c);
        if (!seen[c]) { seen[c] = true; unique++; }
    }
    lower[len < sizeof lower ? len : sizeof lower - 1] = '\0';
    /* Base entropy len*log2(pool), capped by variety — repeats earn quarter credit. */
    double e = pool > 1 ? fmin(raw, ((double)unique + (len - unique) * 0.25) * lp) : 0;
    bool common = false;
    for (size_t i = 0; i < sizeof COMMON / sizeof *COMMON; i++)
        common = common || strcmp(lower, COMMON[i]) == 0;
    if (common) e = 0; /* blocklist hit — zxcvbn's dictionary collapse */
    else {
        for (size_t i = 0; i < sizeof SEQUENCES / sizeof *SEQUENCES; i++)
            for (size_t j = 0; SEQUENCES[i][j + 3]; j++) { /* length-4 windows */
                char win[5] = "";
                memcpy(win, SEQUENCES[i] + j, 4);
                if (strstr(lower, win)) { e -= 12; break; }
            }
        if (e < 0) e = 0;
    }
    int s = len < 4 || e < THRESHOLDS[0] ? 0 : e < THRESHOLDS[1] ? 1 : e < THRESHOLDS[2] ? 2 : e < THRESHOLDS[3] ? 3 : 4;
    return (Analysis){ s, LABELS[s], e, crack_time(e) };
}
int main(void)
{
    const char *samples[] = { "password", "12345678", "Tr0ub4dour&3", "correct horse battery staple", "u2#9Xq!Lp$7wZ" };
    for (size_t i = 0; i < sizeof samples / sizeof *samples; i++) {
        Analysis r = analyse(samples[i]);
        printf("%-26s %d/4 %-9s %6.1f bits  %s\n", samples[i], r.score, r.label, r.entropy, r.crack_time);
    }
}

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 →