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++ (20) port: password strength scoring & feedback. 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).
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <set>
#include <string>
#include <string_view>
#include <vector>

namespace password_strength {
inline constexpr const char* kLabels[5] = {"Very weak", "Weak", "Fair", "Good", "Strong"};
inline constexpr double kThresholds[4] = {28, 36, 60, 128};
inline const std::set<std::string> kCommon = {
    "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"};
inline constexpr std::string_view kSequences[7] = {
    "qwertyuiop", "asdfghjkl", "zxcvbnm", "1234567890", "0987654321", "abcdefg", "gfedcba"};
struct Analysis { int score; std::string label; double entropy; std::string crack_time; };
inline int PoolSize(std::string_view pw) {
    bool lower = false, upper = false, digit = false, sym = false, other = false;
    for (unsigned char c : pw) {
        if (c >= 128) other = true; else if (std::islower(c)) lower = true;
        else if (std::isupper(c)) upper = true; else if (std::isdigit(c)) digit = true; else sym = true;
    }
    return (lower ? 26 : 0) + (upper ? 26 : 0) + (digit ? 10 : 0) + (sym ? 33 : 0) + (other ? 128 : 0);
}
// Does `lower` contain any length-4 window of `seq` (a keyboard/numeric run)?
inline bool ContainsSequence(std::string_view lower, std::string_view seq) {
    for (size_t i = 0; i + 4 <= seq.size(); i++)
        if (lower.find(seq.substr(i, 4)) != std::string_view::npos) return true;
    return false;
}
// Human crack time at 1e10 guesses/s — zxcvbn's offline-fast display.
inline std::string CrackTime(double e) {
    if (e <= 0 || std::pow(2.0, e) / 1e10 < 1) return "instant";
    double s = std::pow(2.0, e) / 1e10;
    static constexpr double kUnits[6] = {1, 60, 3600, 86400, 2592000, 31536000};
    static constexpr const char* kNames[6] = {"second", "minute", "hour", "day", "month", "year"};
    int i = 0; while (i < 5 && s >= kUnits[i + 1]) i++;
    double v = s / kUnits[i]; char buf[40];
    if (v >= 1e9) return "centuries";
    if (v >= 1e6) std::snprintf(buf, sizeof buf, "%.0f million years", v / 1e6);
    else if (v >= 1e3) std::snprintf(buf, sizeof buf, "%.0f thousand years", v / 1e3);
    else std::snprintf(buf, sizeof buf, "%.0f %ss", v, kNames[i]);
    return buf;
}
// Scores 0-4. `user_inputs` seeds the estimator with related tokens (a substring match weakens the score).
inline Analysis Analyse(std::string_view password, const std::vector<std::string>& user_inputs = {}) {
    size_t len = password.size(); int pool = PoolSize(password);
    double lp = pool > 1 ? std::log2(pool) : 0.0;
    size_t unique = std::set<char>(password.begin(), password.end()).size();
    double entropy = pool > 1 ? std::min(len * lp, (static_cast<double>(unique) + (len - unique) * 0.25) * lp) : 0.0;
    std::string lower(password); std::transform(lower.begin(), lower.end(), lower.begin(),
                   [](unsigned char c) { return std::tolower(c); });
    if (kCommon.count(lower)) entropy = 0; // blocklist hit — dictionary collapse
    else {
        for (std::string_view seq : kSequences) if (ContainsSequence(lower, seq)) entropy -= 12;
        for (const std::string& ui : user_inputs) {
            std::string l(ui);
            std::transform(l.begin(), l.end(), l.begin(), [](unsigned char c) { return std::tolower(c); });
            if (l.size() >= 3 && lower.find(l) != std::string::npos) entropy -= 10;
        }
        entropy = std::max(0.0, entropy);
    }
    int score = len < 4 || entropy < kThresholds[0] ? 0 : entropy < kThresholds[1] ? 1 : entropy < kThresholds[2] ? 2 : entropy < kThresholds[3] ? 3 : 4;
    return {score, kLabels[score], entropy, CrackTime(entropy)};
}
}  // namespace password_strength

int main() {
    for (std::string_view pw : {"password", "12345678", "Tr0ub4dour&3", "correct horse battery staple", "u2#9Xq!Lp$7wZ"}) {
        auto r = password_strength::Analyse(pw);
        std::printf("%-26s %d/4 %-9s %6.1f bits  %s\n", std::string(pw).c_str(), r.score,
                    r.label.c_str(), r.entropy, r.crack_time.c_str());
    }
    return 0;
}

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 →