Password Strength Analyser — Zig 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 Zig implementation — the same logic the interactive tool runs, in a shareable, citable form.
// password-strength-analyser — Zig port: password strength scoring & feedback. Language: Zig (0.12+). 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).
const std = @import("std");
const labels = [_][]const u8{ "Very weak", "Weak", "Fair", "Good", "Strong" };
const thresholds = [_]f64{ 28, 36, 60, 128 };
const common = [_][]const u8{
"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",
};
const sequences = [_][]const u8{ "qwertyuiop", "asdfghjkl", "zxcvbnm", "1234567890", "0987654321", "abcdefg", "gfedcba" };
const Analysis = struct { score: u8, label: []const u8, entropy: f64, crack_time: []const u8 };
fn poolSize(pw: []const u8) u32 {
var mask: u8 = 0; // bits: 1 lower, 2 upper, 4 digit, 8 symbol, 16 other
for (pw) |c| {
const bit: u8 = if (c >= 128) 16 else if (std.ascii.isLower(c)) 1 else if (std.ascii.isUpper(c)) 2 else if (std.ascii.isDigit(c)) 4 else 8;
mask |= bit;
}
var pool: u32 = 0;
if (mask & 1 != 0) pool += 26;
if (mask & 2 != 0) pool += 26;
if (mask & 4 != 0) pool += 10;
if (mask & 8 != 0) pool += 33;
if (mask & 16 != 0) pool += 128;
return pool;
}
fn containsSequence(lower: []const u8, seq: []const u8) bool {
var i: usize = 0;
while (i + 4 <= seq.len) : (i += 1)
if (std.mem.indexOf(u8, lower, seq[i .. i + 4]) != null) return true;
return false;
}
var crack_buf: [40]u8 = undefined; // one buffer — fine for a single-threaded demo
fn crackTime(e: f64) []const u8 {
if (e <= 0) return "instant";
const s = std.math.pow(f64, 2, e) / 1e10;
if (s < 1) return "instant";
const units = [_]f64{ 1, 60, 3600, 86400, 2592000, 31536000 };
const names = [_][]const u8{ "second", "minute", "hour", "day", "month", "year" };
var i: usize = 0;
while (i < 5 and s >= units[i + 1]) i += 1;
const v = s / units[i];
if (v >= 1e9) return "centuries";
if (v >= 1e6) return std.fmt.bufPrint(&crack_buf, "{d:.0} million years", .{v / 1e6}) catch "centuries";
if (v >= 1e3) return std.fmt.bufPrint(&crack_buf, "{d:.0} thousand years", .{v / 1e3}) catch "centuries";
return std.fmt.bufPrint(&crack_buf, "{d:.0} {s}s", .{ v, names[i] }) catch "centuries";
}
fn analyse(password: []const u8) Analysis {
const pw = password[0..@min(password.len, 255)]; // display port: first 255 bytes
const len: f64 = @floatFromInt(pw.len); const pool = poolSize(pw);
const lp: f64 = if (pool > 1) std.math.log2(@floatFromInt(pool)) else 0;
var lower_buf: [255]u8 = undefined; const lower = std.ascii.lowerString(&lower_buf, pw);
var seen = [_]bool{false} ** 256; var unique: usize = 0;
for (lower) |c| if (!seen[c]) {
seen[c] = true;
unique += 1;
};
// Base entropy len*log2(pool), capped by variety — repeats earn quarter credit.
const u: f64 = @floatFromInt(unique); var e: f64 = if (pool > 1) @min(len * lp, (u + (len - u) * 0.25) * lp) else 0;
var is_common = false; for (common) |c| if (std.mem.eql(u8, lower, c)) is_common = true;
if (is_common) {
e = 0; // blocklist hit — zxcvbn's dictionary collapse
} else {
for (sequences) |seq| if (containsSequence(lower, seq)) e -= 12;
if (e < 0) e = 0;
}
const score: u8 = if (pw.len < 4 or e < thresholds[0]) 0 else if (e < thresholds[1]) 1 else if (e < thresholds[2]) 2 else if (e < thresholds[3]) 3 else 4;
return .{ .score = score, .label = labels[score], .entropy = e, .crack_time = crackTime(e) };
}
pub fn main() void {
const samples = [_][]const u8{ "password", "12345678", "Tr0ub4dour&3", "correct horse battery staple", "u2#9Xq!Lp$7wZ" };
for (samples) |pw| {
const r = analyse(pw);
std.debug.print("{s:<26} {d}/4 {s:<9} {d:6.1} bits {s}\n", .{ pw, 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 →