Skip to content

Password Generator — Ruby source

Generate cryptographically-random passwords with a CSPRNG using rejection sampling (no modulo bias). Shows live entropy in bits, a 5-tier strength meter, average offline-GPU crack time, and a Pro mode with the entropy formula, a crack-time-vs-length curve, and a 4-scenario attack table. Everything runs locally - nothing is sent anywhere.

This is the Ruby implementation — the same logic the interactive tool runs, in a shareable, citable form.

# password-generator — CSPRNG password generation + entropy scoring. Language: Ruby (3.1+; SecureRandom). Port of src/lib/password.ts.

require 'securerandom'

module PasswordGenerator
  LOWER = 'abcdefghijklmnopqrstuvwxyz'
  UPPER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  NUMBERS = '0123456789'
  SYMBOLS = '!@#$%^&*()-_=+[]{};:,.<>?/'
  AMBIGUOUS = 'O0Il1|' # O/0, I/l/1, pipe — dropped on request

  Options = Struct.new(:length, :upper, :lower, :numbers, :symbols,
                       :exclude_ambiguous, keyword_init: true)

  module_function

  # Concat the selected pools, then drop ambiguous glyphs (mirrors buildCharset).
  def build_charset(o)
    cs = +''
    cs << LOWER if o.lower
    cs << UPPER if o.upper
    cs << NUMBERS if o.numbers
    cs << SYMBOLS if o.symbols
    cs.delete!(AMBIGUOUS) if o.exclude_ambiguous
    cs
  end

  # Uniform index in [0, n): reject uint32 CSPRNG draws >= the largest multiple
  # of n fitting in 2^32, so `% n` carries no modulo bias.
  def unbiased_index(n)
    limit = (1 << 32) - ((1 << 32) % n)
    loop do
      r = SecureRandom.random_bytes(4).unpack1('L>')
      return r % n if r < limit
    end
  end

  # Returns '' for an empty pool or length < 1 — same contract as the TS lib.
  def generate(o)
    cs = build_charset(o)
    return '' if cs.empty? || o.length < 1

    Array.new(o.length) { cs[unbiased_index(cs.length)] }.join
  end

  # Theoretical entropy in bits: length * log2(|alphabet|).
  def entropy_bits(length, charset_size)
    return 0.0 if length <= 0 || charset_size <= 1

    length * Math.log2(charset_size)
  end

  # Strength meter tiers, 1:1 with the TS thresholds.
  def strength_tier(bits)
    return 'very strong' if bits >= 100
    return 'strong' if bits >= 70
    return 'fair' if bits >= 45
    return 'weak' if bits >= 28

    'very weak'
  end

  # Average crack time (s) = 2^(bits-1) / guesses-per-second.
  def crack_time_seconds(bits, guesses_per_second) = 2**(bits - 1) / guesses_per_second
end

o = PasswordGenerator::Options.new(length: 20, upper: true, lower: true,
                                   numbers: true, symbols: true, exclude_ambiguous: true)
cs = PasswordGenerator.build_charset(o)
bits = PasswordGenerator.entropy_bits(o.length, cs.length)
puts "#{PasswordGenerator.generate(o)} — #{bits.round(1)} bits, #{PasswordGenerator.strength_tier(bits)}"

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 →