Secure Token Generator — Zig source
Generate cryptographically-secure random tokens in your browser. Pick the entropy size and format - hex, base32, base64, base62, or alphanumeric - and see the real strength in bits. Runs entirely client-side.
This is the Zig implementation — the same logic the interactive tool runs, in a shareable, citable form.
// token-generator — cryptographically-secure random tokens in hex/base32/base64/base62/... alphabets, with unbiased symbol selection and entropy/strength estimates. Language: Zig (0.13, standard library only — std.crypto.random, the kernel-backed CSPRNG). Port of src/lib/token-generator.ts — same logic as this dir's javascript.js; the full 8-entry alphabet table, custom alphabets and injectable RNG omitted for the 80-line budget (see javascript.js / python.py).
const std = @import("std");
const HEX = "0123456789abcdef";
const BASE32 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; // RFC 4648
const BASE64URL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
const BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
/// Resolve an alphabet name to its symbol string; unknown -> hex (the default).
fn resolveAlphabet(name: []const u8) []const u8 {
if (std.mem.eql(u8, name, "base32")) return BASE32;
if (std.mem.eql(u8, name, "base64url")) return BASE64URL;
if (std.mem.eql(u8, name, "base62")) return BASE62;
return HEX;
}
/// Characters needed to carry `bytes` bytes of entropy through `size` symbols.
fn outputLength(bytes: f64, size: usize) usize {
if (bytes < 1 or size < 2) return 0;
const bits = bytes * 8.0 / std.math.log2(@as(f64, @floatFromInt(size)));
return @intFromFloat(@ceil(bits));
}
/// Pick alphabet[0..n] WITHOUT modulo bias: naive draw % size favors trailing
/// symbols whenever size does not divide the draw range (base62 etc.), so any
/// draw at or above the largest multiple of size fitting in 32 bits is
/// rejected and redrawn. The 64-redraw cap stops a broken RNG from looping
/// forever — same guard as the TS/Python reference.
fn selectUnbiased(alphabet: []const u8, out: []u8) void {
const size: u32 = @intCast(alphabet.len);
const limit: u32 = std.math.maxInt(u32) / size * size;
for (out, 0..) |*slot, i| {
_ = i;
var x = std.crypto.random.int(u32); // one secure 32-bit draw
var redraws: u32 = 0;
while (x >= limit and redraws < 64) : (redraws += 1)
x = std.crypto.random.int(u32);
slot.* = alphabet[x % size];
}
}
/// Bucket an entropy estimate (bits) into the tool's strength tiers:
/// weak <64 · fair 64-127 · strong 128-255 · very strong >=256.
fn strengthLabel(bits: f64) []const u8 {
if (bits < 64) return "weak";
if (bits < 128) return "fair";
if (bits < 256) return "strong";
return "very strong";
}
/// Demo: the island's three showcase rows — 16-byte hex, 20-byte base62,
/// 32-byte base32 — each labeled with its real strength in bits.
pub fn main() !void {
var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
const out = bw.writer();
const Demo = struct { name: []const u8, bytes: f64 };
const demos = [_]Demo{
.{ .name = "hex", .bytes = 16 },
.{ .name = "base62", .bytes = 20 },
.{ .name = "base32", .bytes = 32 },
};
var token: [64]u8 = undefined; // longest demo: 52 chars (32 bytes, base32)
for (demos) |d| {
const alphabet = resolveAlphabet(d.name);
const n = outputLength(d.bytes, alphabet.len);
selectUnbiased(alphabet, token[0..n]);
const bits: f64 = @as(f64, @floatFromInt(n)) * std.math.log2(@as(f64, @floatFromInt(alphabet.len)));
try out.print("{s} {d:.0} bytes -> {s} ({d:.0} bits, {s})\n", .{ d.name, d.bytes, token[0..n], bits, strengthLabel(bits) });
}
try bw.flush();
}
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 →