[Ruby] Command-line Password Generator

A Ruby script for generating sequential passwords to pipe into other tools. Supports command-line parameters to customize the password length range, character set, and password seed. Generated passwords are output to stdout. While running, press ‘spacebar’ to output generator stats to stderr.

Password Generator
Usage: generator.rb [options]
-v, –verbose Output more information
-l, –lowercase Include lowercase letters in seeds
-u, –uppercase Include uppercase letters in seeds
-n, –numbers Include numbers in seeds
-s, –special Include special characters in seeds
-a, –all Include all characters in seeds
-m, –min NUMBER Set minimum seed length(>0)
-M, –max NUMBER Set maximum seed length(>minimum)
-w, –wifi Set min and max to acceptable wifi seed length
-S, –seed SEED Set starting seed string
-h, –help Display this screen

#!/usr/bin/env ruby
require 'optparse'
require 'io/wait'

$interrupted = false

trap("INT") { $interrupted = true}
options = {}
optparse = OptionParser.new do |opts|
  opts.banner = "Password Generator\nUsage: generator.rb [options]"
  
  #Defines the command line options
  options[:verbose] = false
  opts.on('-v','--verbose', 'Output more information') do
    options[:verbose] = true
  end
  options[:lowercase] = false
  opts.on('-l', '--lowercase', 'Include lowercase letters in seeds') do
    options[:lowercase] = true
  end
  options[:uppercase] = false
  opts.on('-u', '--uppercase', 'Include uppercase letters in seeds') do
    options[:uppercase] = true
  end
  options[:numbers] = false
  opts.on('-n', '--numbers', 'Include numbers in seeds') do
    options[:numbers] = true
  end
  options[:special] = false
  opts.on('-s', '--special', 'Include special characters in seeds') do
    options[:special] = true
  end
  options[:all] = false
  opts.on('-a', '--all', 'Include all characters in seeds') do
    options[:all] = true
    options[:lowercase] = true
    options[:uppercase] = true
    options[:numbers] = true
    options[:special] = true
  end
  options[:min] = 1
  opts.on('-m NUMBER', '--min NUMBER', 'Set minimum seed length(>0)') do |i|
    if i.to_i > 0
      options[:min] = i.to_i
    else
      puts "Invalid Mimimum, must be greater than 0."
      exit
    end
  end
  options[:max] = 10
  opts.on('-M NUMBER', '--max NUMBER', 'Set maximum seed length(>minimum)') do |i|
    if i.to_i >= options[:min]  
	options[:max] = i.to_i
    else
      puts "Invalid Maximum, must be greater than or equal to the minimum."
      exit
    end
  end
  options[:wifi] = false
  opts.on('-w', '--wifi', 'Set min and max to acceptable wifi seed length') do
    options[:min] = 8 
    options[:max] = 63
  end
  
  options[:seed] = ""
  opts.on('-S SEED', '--seed SEED', 'Set starting seed string') do |i|
    options[:seed] = i
    puts "Start seed: " + options[:seed]
  end

  opts.on('-h', '--help', 'Display this screen' ) do
    puts opts
    exit
  end


end
optparse.parse!

#lowercase = "abcdefghijklmnopqrstuvwxyz"
lowercase = "etaoinsrhdlucmfywgpbvkxqjz"
#uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
uppercase = "ETAOINSRHDLUCMFYWGPBVKXQJZ"
numbers = "1234567890"
special = "\-\=\`\~\!\@\#\$\%\^\&\*\(\)\_\+\,\.\/\<\>\?\;\'\:\"\[\]\{\}\\"
total = 0
$seeder = ""
$seeder = lowercase if options[:lowercase] == true
$seeder += uppercase if options[:uppercase]
$seeder += numbers if options[:numbers]
$seeder += special if options[:special]
$start_seed =  (options[:seed].length >0 ? options[:seed] : "")
0.upto($start_seed.length-1) {|i|
  if $seeder.index($start_seed[i]).nil?
    puts "Seed character (" + $start_seed[i].chr + ")  not in character set, please specify additional character sets."
    exit
  end
                             }
  
$seeder_length = $seeder.length
#puts $seeder
if $seeder_length == 0
  puts "Must specify characters to use. See --help for available options."
  exit
end
#puts $seeder
$start_time = Time.new
$max_length = options[:max]
$current_length = options[:min]
$current_slot = 0
$candidates = []
$password = ""
$total = 0

def initialize_candidates
  0.upto($max_length-1) {|i| $candidates[i] = 0}
  if $start_seed.length > 0 
    0.upto($start_seed.length-1) {|i| $candidates[i] = $seeder.index($start_seed[i])}
  end
end

def increment_letter
	$candidates[$current_slot] += 1
	
	if $candidates[$current_slot] >= $seeder_length && $current_slot > 0
		$candidates[$current_slot] = 0
		$current_slot -= 1
		increment_letter
	end	
	
end

def input?
  begin
    c = $stdin.getc
    return true if c == "q"
    show_stats if c == " "
    false
    rescue Errno::EINTR
      false
    rescue Errno::EAGAIN
      false
    rescue EOFError
      true
  end
end

def show_stats
    $stderr.puts "Generator Stats - words: " + $total.to_s + " current: " + $password + " w/s: " + ($total/(Time.now - $start_time)).round.to_s + " run time: " + (Time.new - $start_time).to_i.to_s + " second(s)"#Time.new(Time.now - $start_time).strftime("%T")
    
end

def exiting
  show_stats
  $stderr.puts "Exiting...                                                                   "
  exit
end

#system("stty raw -echo")
initialize_candidates
#Curses.noecho

while $current_length <= $max_length
  $password = ""
  $current_slot = $current_length-1
  0.upto($current_length-1) {|i| $password = $password + $seeder[$candidates[i]].chr}
  $stdout.puts $password
  $total += 1
  increment_letter
  if $candidates[0] >= $seeder_length
    $current_length += 1
    initialize_candidates
  end
  exiting if input? if $stdin.ready?#input?
  exiting if $interrupted == true
end

Justin C. (Author Profile)

Justin C.

Technology Enthusiast

I’m a Soldier by day and coder by night. A life long learner of cyber security, penetration testing, machine learning, circuit design, art, and photography.

Liked it? Take a second to support Justin on Patreon!
%d bloggers like this: