mkpw

[root]/ src / main.zig

3.0KB

raw
/// mkpw
/// Author: Sebastian Michalk <sebastian.michalk@pm.me>
/// ISC License
/// (c) 2026

const std = @import("std");
const os = @import("os");
const exit = std.process.exit;
const srand = std.crypto.random;

pub var CHARSET = blk: {
    var chars: [95]u8 = undefined;
    for (0..95) |i| {
        chars[i] = 32 + @as(u8, @intCast(i));
    }
    break :blk chars;
};

pub fn die(comptime fmt: []const u8, args: anytype) noreturn {
    const stderr = std.fs.File.stderr().deprecatedWriter();
    stderr.print(fmt, args) catch {};
    exit(1);
}

pub fn calc_entropy(s: usize) f64 {
    const n: f64 = 95.0;
    const l: f64 = @floatFromInt(s);
    return l * std.math.log2(n);
}

pub fn mkpw(pwlen: u32, buffer: []u8) ![]u8 {
    if (pwlen == 0 or pwlen > 256) {
        die("mkpw: error: length must be greater than 0 and less or equal 256.\n", .{});
    }

    const target_len = @as(usize, @intCast(pwlen));
    if (target_len > buffer.len) {
        die("memcpy\n", .{});
    }

    var i: usize = 0;
    while (i < target_len) : (i += 1) {
        const idx = srand.uintLessThan(usize, CHARSET.len);
        buffer[i] = CHARSET[idx];
    }

    return buffer[0..target_len];
}

pub fn printpw(buf: []u8) !void {
 
    // Zig 0.15.1 // TODO different way to print to stdout
    const stdout = std.fs.File.stdout().deprecatedWriter();
    const entropy: f64 = calc_entropy(buf.len);
    try stdout.print("{s}\n", .{buf});
    try stdout.print("mkpw: entropy: \x1b[4m{d:.1} bits\x1b[0m.\n", .{entropy});
    std.crypto.secureZero(u8, buf);
    exit(0);
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const alloc = gpa.allocator();

    const args = try std.process.argsAlloc(alloc);
    defer std.process.argsFree(alloc, args);

    var i: usize = 1;
    var pwlen: u32 = 10;
    var pwbuf = [_]u8{0} ** 256;

    if (args.len < 2) {
        const pw = try mkpw(pwlen, &pwbuf);
        try printpw(pw);
    }

    while (i < args.len) : (i += 1) {
        const arg = args[i];
        if (std.mem.eql(u8, arg, "-h") or std.mem.eql(u8, arg, "--help")) {
            die("mkpw: usage: -l or --length <uint>\n", .{});
        }
        if (std.mem.eql(u8, arg, "-l") or std.mem.eql(u8, arg, "--length")) {
            i += 1;
            if (i >= args.len) {
                die("mkpw: error: length requires a value\n", .{});
            }
            pwlen = std.fmt.parseInt(u32, args[i], 10) catch |err| {
                switch (err) {
                    error.InvalidCharacter => {
                        die("mkpw: error: '{s}' is not a valid int\n", .{args[i]});
                    },
                    error.Overflow => {
                        die("mkpw: error: number too large or negative number\n", .{});
                    },
                }
                exit(1);
            };
        } else {
            die("mkpw: usage: -l or --length <uint>\n", .{});
        }
        const pw = try mkpw(pwlen, &pwbuf);
        try printpw(pw);
    }
}