Secure Token Generator — Python 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 Python implementation — the same logic the interactive tool runs, in a shareable, citable form.
#!/usr/bin/env python3
# ─────────────────────────────────────────────────────────────────────────────
# Secure token generator — Python polyglot showcase port.
# Language: Python 3.10+ (standard library only — no pip dependencies).
#
# CosmoDev polyglot showcase port of `token-generator`, ported from the
# canonical TypeScript logic in src/lib/token-generator.ts.
#
# This is display source — part of CosmoDev's polyglot tool pages, where each
# tool's pure logic is shown side-by-side in many languages.
#
# Design:
# - Pure logic with an *injectable* RNG, so generation is unit-testable
# without touching the secure RNG. Callers pass None for the CSPRNG
# default; tests pass a seeded generator for exact, reproducible output.
# - Each output symbol is selected without modulo bias via rejection
# sampling (see `constant_time_select`), so even non-power-of-two
# alphabets like base62 are unbiased.
#
# CSPRNG: Python's `secrets` module is the documented primitive for
# cryptographically secure randomness (it wraps os.urandom / getrandom).
# ─────────────────────────────────────────────────────────────────────────────
from __future__ import annotations
import math
import secrets
from dataclasses import dataclass
from typing import Callable, Literal, Optional
# Strength labels as a Literal type — mirrors the TS string-literal union.
StrengthLabel = Literal["weak", "fair", "strong", "very strong"]
#: The fixed alphabet strings. "custom" is intentionally absent — its symbols
#: are caller-supplied via ``GenerateOptions.custom_alphabet``.
ALPHABETS: dict[str, str] = {
"hex": "0123456789abcdef",
"hex-upper": "0123456789ABCDEF",
"base32": "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", # RFC 4648
"base32-crockford": "0123456789ABCDEFGHJKMNPQRSTVWXYZ", # Crockford (no I/L/O/U)
"base64": "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
"base64url": "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
"base62": "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
"alphanumeric": "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
}
#: Convenience encoding aliases mapping 1:1 onto a fixed alphabet.
_ENCODING_TO_ALPHABET: dict[str, str] = {
"hex": "hex",
"base32": "base32",
"base64": "base64",
"base64url": "base64url",
"base62": "base62",
"alphanumeric": "alphanumeric",
}
@dataclass
class GenerateOptions:
"""Controls token generation. All fields optional.
``alphabet`` wins over ``encoding`` when both are given. ``custom_alphabet``
is used only when ``alphabet == "custom"``. ``rng`` overrides the default
CSPRNG (useful for deterministic tests).
"""
alphabet: Optional[str] = None
custom_alphabet: Optional[str] = None
encoding: Optional[str] = None
rng: Optional[Callable[[], float]] = None
def _default_rng() -> float:
"""Draw a uniform float in [0, 1) using the kernel CSPRNG.
``secrets.randbelow(2**32)`` returns an unbiased integer in [0, 2^32)
backed by the OS cryptographically secure RNG (getrandom/BCryptGenRandom).
Dividing by 2^32 maps every outcome uniformly into the unit interval.
"""
return secrets.randbelow(1 << 32) / (1 << 32)
def resolve_alphabet(
opts: Optional[GenerateOptions] = None,
encoding: Optional[str] = None,
) -> str:
"""Resolve the effective alphabet string from options and an encoding.
Precedence: explicit ``opts.alphabet`` → ``encoding`` → ``"hex"``. A
``"custom"`` alphabet with no/empty ``custom_alphabet`` resolves to ``""``
(an invalid, empty set).
"""
if opts is not None and opts.alphabet == "custom":
return opts.custom_alphabet or ""
if opts is not None and opts.alphabet:
return ALPHABETS.get(opts.alphabet, "")
enc = encoding if encoding is not None else (opts.encoding if opts is not None else None)
if enc and enc in _ENCODING_TO_ALPHABET:
return ALPHABETS.get(_ENCODING_TO_ALPHABET[enc], "")
return ALPHABETS["hex"]
def constant_time_select(
alphabet: str,
n: int,
rng: Optional[Callable[[], float]] = None,
) -> str:
"""Build an ``n``-character string from ``alphabet`` WITHOUT modulo bias.
Naive ``draw % size`` is biased whenever ``size`` does not divide the draw
range: for base62 the trailing symbols would be slightly over-represented.
Instead we reject any 32-bit draw landing in the uneven remainder
(``>= limit``) and redraw, keeping every symbol exactly equally likely.
The ``guard`` cap stops a pathological/constant RNG from looping forever.
"""
r = rng or _default_rng
size = len(alphabet)
if size < 1 or n is None or n < 1:
return ""
# Largest multiple of ``size`` that fits in [0, 2^32-1]. Draws at or above
# this boundary map unevenly under ``% size``, so we redraw.
limit = (0xFFFFFFFF // size) * size
out: list[str] = []
for _ in range(n):
x = r() * 0x100000000 # [0, 2^32)
guard = 0
while x >= limit and guard < 64:
x = r() * 0x100000000
guard += 1
out.append(alphabet[math.floor(x) % size])
return "".join(out)
def output_length(bytes_: float, alphabet_size: int) -> int:
"""Characters needed to carry ``bytes_`` bytes of entropy through an
alphabet of ``alphabet_size`` symbols."""
if not math.isfinite(bytes_) or bytes_ < 1 or alphabet_size < 2:
return 0
return math.ceil((bytes_ * 8) / math.log2(alphabet_size))
def generate_token(
bytes_: float,
opts: Optional[GenerateOptions] = None,
encoding: Optional[str] = None,
) -> str:
"""Generate a token carrying ``bytes_`` bytes of underlying entropy.
The token is rendered through ``opts.alphabet`` (or ``encoding``). Each
character is sampled uniformly without modulo bias, so output is unbiased
even for base62. Returns ``""`` for invalid input (non-positive or
non-finite bytes, an alphabet under 2 symbols).
Example: ``generate_token(16, GenerateOptions(alphabet="hex"))`` → 32 hex
chars (128 bits).
"""
if not math.isfinite(bytes_) or bytes_ < 1:
return ""
alphabet = resolve_alphabet(opts, encoding)
if len(alphabet) < 2:
return ""
n = output_length(bytes_, len(alphabet))
rng = opts.rng if opts is not None else None
return constant_time_select(alphabet, n, rng)
def estimate_entropy(bytes_: float, alphabet_size: int) -> float:
"""Entropy (in bits) of a token of ``bytes_`` entropy in a ``size``-symbol
alphabet.
Equals ``output_length * log2(size)``, which is ``>= bytes * 8`` because
the character count is rounded up to the next whole symbol.
"""
if not math.isfinite(bytes_) or bytes_ < 1 or alphabet_size < 2:
return 0.0
return output_length(bytes_, alphabet_size) * math.log2(alphabet_size)
def strength_label(entropy_bits: float) -> StrengthLabel:
"""Bucket an entropy estimate (bits) into a human strength label.
Tiers: weak <64 · fair 64–127 · strong 128–255 · very strong ≥256.
"""
if not math.isfinite(entropy_bits) or entropy_bits < 64:
return "weak"
if entropy_bits < 128:
return "fair"
if entropy_bits < 256:
return "strong"
return "very strong"
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 →