├── .gitignore ├── lib ├── sgpass │ └── version.rb └── sgpass.rb ├── HISTORY.md ├── sgpass.gemspec ├── README.md └── bin └── sgpass /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | -------------------------------------------------------------------------------- /lib/sgpass/version.rb: -------------------------------------------------------------------------------- 1 | module SGPass 2 | VERSION = "0.1.1" 3 | 4 | def self.version 5 | VERSION 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | v0.1.1 - Aug 03, 2011 2 | --------------------- 3 | 4 | ### Fixed: 5 | * Fixed formatting in the help output. 6 | 7 | v0.1.0 - Aug 03, 2011 8 | --------------------- 9 | 10 | ### Changed: 11 | * Passwords are not shown in the console when prompted. 12 | * Added the `-c` option to copy the result to clipboard. 13 | * Passwords are now asked twice for confirmation. 14 | * Added the `-f` option to just ask for the password once. 15 | 16 | v0.0.1 - May 09, 2011 17 | --------------------- 18 | 19 | Initial release. 20 | -------------------------------------------------------------------------------- /sgpass.gemspec: -------------------------------------------------------------------------------- 1 | require "./lib/sgpass/version" 2 | Gem::Specification.new do |s| 3 | s.name = "sgpass" 4 | s.version = SGPass.version 5 | s.summary = "Password generator based on hash algorithms." 6 | s.description = "SGPass uses a hash algorithm to transform a master password into unique, complex passwords for the Web sites you visit. This is a Ruby port of www.supergenpass.com." 7 | s.authors = ["Rico Sta. Cruz"] 8 | s.email = ["rico@sinefunc.com"] 9 | s.homepage = "http://github.com/rstacruz/sgpass" 10 | s.files = Dir["{lib,bin,test}/**/*", "*.md", "Rakefile"].reject { |f| File.directory?(f) } 11 | s.executables = ["sgpass"] 12 | 13 | s.add_dependency 'highline', '~> 1.6.2' 14 | s.add_dependency 'clipboard', '~> 0.9.9' 15 | end 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SuperGenPass for Ruby 2 | 3 | ## Command line usage 4 | 5 | Install it: 6 | 7 | $ gem install sgpass 8 | 9 | To generate a password for a site, use: 10 | 11 | $ sgpass facebook.com 12 | Password: ****** 13 | h8GCua3DxC 14 | 15 | To copy it to clipboard: 16 | 17 | $ sgpass facebook.com -c 18 | Password: ****** 19 | Done. Your password has been copied to the clipboard. 20 | 21 | ## Ruby usage 22 | 23 | Or from your Ruby app, you may: 24 | 25 | ```ruby 26 | require 'sgpass' 27 | str = SGPass.generate('hunter2', 'http://www.facebook.com') #=> "vXzettvkI2" 28 | ``` 29 | 30 | ## More usage notes 31 | 32 | You may even pass full URL's and `sgpass` will figure it out: 33 | 34 | $ sgpass http://www.facebook.com/profile.php 35 | Password: ****** 36 | h8GCua3DxC 37 | 38 | If you don't want to to be asked for your password, pass it straight from the 39 | command line: 40 | 41 | $ sgpass facebook.com MyPassword 42 | h8GCua3DxC 43 | 44 | ## Acknowledgements 45 | 46 | Gem by [Rico Sta. Cruz](http://www.ricostacruz.com), based on the original 47 | [supergenpass.com](http://www.supergenpass.com) JavaScript version. 48 | 49 | -------------------------------------------------------------------------------- /bin/sgpass: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require File.expand_path('../../lib/sgpass', __FILE__) 3 | require 'highline' 4 | 5 | class << ARGV 6 | def extract(what) i = index(what) and slice!(i, 2)[1] end; 7 | end 8 | 9 | def print_usage(options={}) 10 | full = options[:full] 11 | cmd = options[:cmd] || File.basename($0) 12 | 13 | $stderr.write "Usage: #{cmd} URL [PASSWORD] [-c] [-f]\n" 14 | if full 15 | $stderr.write "\n" 16 | $stderr.write "Generates a site-specific password for the given URL and master password.\n" 17 | $stderr.write "If no password is given, you will be prompted for one.\n" 18 | $stderr.write "\n" 19 | $stderr.write "Options:\n" 20 | $stderr.write " -c, --copy Copies the result to the clipboard.\n" 21 | $stderr.write " -f, --fast Prompts password just once instead of twice.\n" 22 | $stderr.write " -l [length] Change the password length. (default: 10)\n" 23 | $stderr.write "\n" 24 | $stderr.write "Examples:\n" 25 | $stderr.write " #{cmd} twitter.com\n" 26 | $stderr.write " #{cmd} facebook.com HelloThere20\n" 27 | $stderr.write " #{cmd} http://admin.site.com/page.html hunter2\n" 28 | end 29 | end 30 | 31 | $options = { 32 | :copy => ARGV.delete('-c') || ARGV.delete('--copy'), 33 | :fast => ARGV.delete('-f') || ARGV.delete('--fast'), 34 | :length => ARGV.extract('-l') || ARGV.extract('--length') || 10 35 | } 36 | 37 | def output(str) 38 | if $options[:copy] 39 | require 'clipboard' 40 | Clipboard.copy str 41 | $stderr.write "Done. Your password is in the clipboard.\n" 42 | else 43 | puts str 44 | end 45 | end 46 | 47 | def ask_passwd 48 | ask = lambda { |message| HighLine.new.ask(message) { |q| q.echo = '*' } } 49 | 50 | if $options[:fast] 51 | ask.call "Password: " 52 | else 53 | one = 0 54 | two = nil 55 | 56 | while one != two 57 | $stderr.write "Passwords don't match. Try again.\n\n" if one != 0 58 | 59 | one = ask.call "Password: " 60 | two = ask.call "Repeat: " 61 | end 62 | 63 | one 64 | end 65 | end 66 | 67 | if ARGV == ['--help'] || ARGV == ['-h'] || ARGV.empty? 68 | print_usage :full => true 69 | 70 | elsif ARGV.size == 1 71 | unless (4..24).include? $options[:length].to_i 72 | puts "Length must be between 4 and 24." 73 | exit 256 74 | end 75 | 76 | uri, _ = ARGV 77 | passwd = ask_passwd 78 | 79 | output SGPass.generate(passwd, uri, length: $options[:length]) 80 | 81 | elsif ARGV.size != 2 82 | print_usage 83 | exit 256 84 | 85 | else 86 | uri, passwd = ARGV 87 | output SGPass.generate(passwd, uri) 88 | end 89 | -------------------------------------------------------------------------------- /lib/sgpass.rb: -------------------------------------------------------------------------------- 1 | require 'digest' 2 | require 'base64' 3 | 4 | # Password generator. 5 | # 6 | # == Common usage 7 | # 8 | # SGPass.generate('password', 'facebook.com') 9 | # 10 | # # Specified length 11 | # SGPass.generate('password', 'facebook.com', length: 12) 12 | # 13 | # # Disable subdomain retrieval 14 | # SGPass.generate('password', 'facebook.com', tld: false) 15 | # 16 | module SGPass 17 | # Generates a password. 18 | def self.generate(password, domain, options={}) 19 | length = (options[:length] || 10).to_i 20 | 21 | raise StandardError, "Length must be between 4 and 24." unless (4..24).include?(length) 22 | 23 | domain = get_top_domain(domain, !(options[:tld] == false)) 24 | 25 | str = "#{password}:#{domain}" 26 | 27 | i = 0 28 | while i < 10 || !strong_enough?(str[0...length]) 29 | str = b64_md5(str) 30 | i += 1 31 | end 32 | 33 | str[0...length] 34 | end 35 | 36 | # Returns the domain 37 | # 38 | # == Examples 39 | # "http://admin.site.com/home.html" => "site.com" 40 | # "http://admin.site.com/home.html", false => "admin.site.com" 41 | # 42 | def self.get_top_domain(uri, enable_tld=true) 43 | hostname = get_hostname(uri.downcase) 44 | parts = get_host_parts(hostname) 45 | 46 | if parts.size <= 2 || !enable_tld 47 | hostname 48 | else 49 | uri = parts[-2..-1].join('.') 50 | uri = "#{parts[-3]}.#{uri}" if TLD_LIST.include?(uri) 51 | uri 52 | end 53 | end 54 | 55 | private 56 | def self.strong_enough?(str) 57 | str.match(/^[a-z]/) && str.match(/[0-9]/) && str.match(/[A-Z]/) 58 | end 59 | 60 | def self.b64_md5(str) 61 | Digest::MD5.base64digest(str).tr('=+/', 'A98') 62 | end 63 | 64 | # "facebook.com" => ["facebook", "com"] 65 | def self.get_host_parts(host) 66 | host.match(IP_MATCH) ? [host] : host.split('.') 67 | end 68 | 69 | # "http://www.facebook.com/home.php" => "www.facebook.com" 70 | def self.get_hostname(uri) 71 | m = uri.match(URI_MATCH) || uri.match(/^([^\/:]+)/) 72 | m ? m[1] : uri 73 | end 74 | 75 | URI_MATCH = /^(?:http|https|ftp|ftps|webdav|gopher|rtsp|irc|nntp|pop|imap|smtp):\/\/([^\/:]+)/ 76 | IP_MATCH = /^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})$/ 77 | TLD_LIST = %w[ac.ac com.ac edu.ac gov.ac net.ac mil.ac org.ac com.ae net.ae org.ae gov.ae ac.ae co.ae sch.ae pro.ae com.ai org.ai edu.ai gov.ai com.ar net.ar org.ar gov.ar mil.ar edu.ar int.ar co.at ac.at or.at gv.at priv.at com.au gov.au org.au edu.au id.au oz.au info.au net.au asn.au csiro.au telememo.au conf.au otc.au id.au com.az net.az org.az com.bb net.bb org.bb ac.be belgie.be dns.be fgov.be com.bh gov.bh net.bh edu.bh org.bh com.bm edu.bm gov.bm org.bm net.bm adm.br adv.br agr.br am.br arq.br art.br ato.br bio.br bmd.br cim.br cng.br cnt.br com.br coop.br ecn.br edu.br eng.br esp.br etc.br eti.br far.br fm.br fnd.br fot.br fst.br g12.br ggf.br gov.br imb.br ind.br inf.br jor.br lel.br mat.br med.br mil.br mus.br net.br nom.br not.br ntr.br odo.br org.br ppg.br pro.br psc.br psi.ov.au org.au edu.au id.au oz.au info.au net.au asn.au csiro.au telememo.au conf.au otc.au id.au com.az net.az org.az com.bb net.bb org.bb ac.be belgie.be dns.be fgov.be com.bh gov.bh net.bh edu.bh org.bh com.bm edu.bm gov.bm org.bm net. gov.cn net.cn org.cn ac.cn ah.cn bj.cn cq.cn gd.cn gs.cn gx.cn gz.cn hb.cn he.cn hi.cn hk.cn hl.cn hn.cn jl.cn js.cn ln.cn mo.cn nm.cn nx.cn qh.cn sc.cn sn.cn sh.cn sx.cn tj.cn tw.cn xj.cn xz.cn yn.cn zj.cn arts.co com.co edu.co firm.co gov.co info.co int.co nom.co mil.co org.co rec.co store.co web.co ac.cr co.cr ed.cr fi.cr go.cr or.cr sa.cr com.cu net.cu org.cu ac.cy com.cy gov.cy net.cy org.cy co.dk art.do com.do edu.do gov.do gob.do org.do mil.do net.do sld.do web.do com.dz org.dz net.dz gov.dz edu.dz ass.dz pol.dz art.dz com.ec k12.ec edu.ec fin.ec med.ec gov.ec mil.ec org.ec net.ec com.ee pri.ee fie.ee org.ee med.ee com.eg edu.eg eun.eg gov.eg net.eg org.eg sci.eg com.er net.er org.er edu.er mil.er gov.er ind.er com.es org.es gob.es edu.es nom.es com.et gov.et org.et edu.et net.et biz.et name.et info.et ac.fj com.fj gov.fj id.fj org.fj school.fj com.fk ac.fk gov.fk net.fk nom.fk org.fk asso.fr nom.fr barreau.fr com.fr prd.fr presse.fr tm.fr aeroport.fr assedic.fr avocat.fr avoues.fr cci.fr chambagri.fr chirurgiens-dentistes.fr experts-comptables.fr geometre-expert.fr gouv.fr greta.fr huissier-justice.fr medecin.fr notaires.fr pharmacien.fr port.fr veterinaire.fr com.ge edu.ge gov.ge mil.ge net.ge org.ge pvt.ge co.gg org.gg sch.gg ac.gg gov.gg ltd.gg ind.gg net.gg alderney.gg guernsey.gg sark.gg com.gr edu.gr gov.gr net.gr org.gr com.gt edu.gt net.gt gob.gt org.gt mil.gt ind.gt com.gu edu.gu net.gu org.gu gov.gu mil.gu com.hk net.hk org.hk idv.hk gov.hk edu.hk co.hu 2000.hu erotika.hu jogasz.hu sex.hu video.hu info.hu agrar.hu film.hu konyvelo.hu shop.hu org.hu bolt.hu forum.hu lakas.hu suli.hu priv.hu casino.hu games.hu media.hu szex.hu sport.hu city.hu hotel.hu news.hu tozsde.hu tm.hu erotica.hu ingatlan.hu reklam.hu utazas.hu ac.id co.id go.id mil.id net.id or.id co.il net.il org.il ac.il gov.il k12.il muni.il idf.il co.im net.im org.im ac.im lkd.co.im gov.im nic.im plc.co.im co.in net.in ac.in ernet.in gov.in nic.in res.in gen.in firm.in mil.in org.in ind.in ac.ir co.ir gov.ir id.ir net.ir org.ir sch.ir ac.je co.je net.je org.je gov.je ind.je jersey.je ltd.je sch.je com.jo org.jo net.jo gov.jo edu.jo mil.jo ad.jp ac.jp co.jp go.jp or.jp ne.jp gr.jp ed.jp lg.jp net.jp org.jp gov.jp hokkaido.jp aomori.jp iwate.jp miyagi.jp akita.jp yamagata.jp fukushima.jp ibaraki.jp tochigi.jp gunma.jp saitama.jp chiba.jp tokyo.jp kanagawa.jp niigata.jp toyama.jp ishikawa.jp fukui.jp yamanashi.jp nagano.jp gifu.jp shizuoka.jp aichi.jp mie.jp shiga.jp kyoto.jp osaka.jp hyogo.jp nara.jp wakayama.jp tottori.jp shimane.jp okayama.jp hiroshima.jp yamaguchi.jp tokushima.jp kagawa.jp ehime.jp kochi.jp fukuoka.jp saga.jp nagasaki.jp kumamoto.jp oita.jp miyazaki.jp kagoshima.jp okinawa.jp sapporo.jp sendai.jp yokohama.jp kawasaki.jp nagoya.jp kobe.jp kitakyushu.jp utsunomiya.jp kanazawa.jp takamatsu.jp matsuyama.jp com.kh net.kh org.kh per.kh edu.kh gov.kh mil.kh ac.kr co.kr go.kr ne.kr or.kr pe.kr re.kr seoul.kr kyonggi.kr com.kw net.kw org.kw edu.kw gov.kw com.la net.la org.la com.lb org.lb net.lb edu.lb gov.lb mil.lb com.lc edu.lc gov.lc net.lc org.lc com.lv net.lv org.lv edu.lv gov.lv mil.lv id.lv asn.lv conf.lv com.ly net.ly org.ly co.ma net.ma org.ma press.ma ac.ma com.mk com.mm net.mm org.mm edu.mm gov.mm com.mn org.mn edu.mn gov.mn museum.mn com.mo net.mo org.mo edu.mo gov.mo com.mt net.mt org.mt edu.mt tm.mt uu.mt com.mx net.mx org.mx gob.mx edu.mx com.my org.my gov.my edu.my net.my com.na org.na net.na alt.na edu.na cul.na unam.na telecom.na com.nc net.nc org.nc ac.ng edu.ng sch.ng com.ng gov.ng org.ng net.ng gob.ni com.ni net.ni edu.ni nom.ni org.ni com.np net.np org.np gov.np edu.np ac.nz co.nz cri.nz gen.nz geek.nz govt.nz iwi.nz maori.nz mil.nz net.nz org.nz school.nz com.om co.om edu.om ac.om gov.om net.om org.om mod.om museum.om biz.om pro.om med.om com.pa net.pa org.pa edu.pa ac.pa gob.pa sld.pa edu.pe gob.pe nom.pe mil.pe org.pe com.pe net.pe com.pg net.pg ac.pg com.ph net.ph org.ph mil.ph ngo.ph aid.pl agro.pl atm.pl auto.pl biz.pl com.pl edu.pl gmina.pl gsm.pl info.pl mail.pl miasta.pl media.pl mil.pl net.pl nieruchomosci.pl nom.pl org.pl pc.pl powiat.pl priv.pl realestate.pl rel.pl sex.pl shop.pl sklep.pl sos.pl szkola.pl targi.pl tm.pl tourism.pl travel.pl turystyka.pl com.pk net.pk edu.pk org.pk fam.pk biz.pk web.pk gov.pk gob.pk gok.pk gon.pk gop.pk gos.pk edu.ps gov.ps plo.ps sec.ps com.pt edu.pt gov.pt int.pt net.pt nome.pt org.pt publ.pt com.py net.py org.py edu.py com.qa net.qa org.qa edu.qa gov.qa asso.re com.re nom.re com.ro org.ro tm.ro nt.ro nom.ro info.ro rec.ro arts.ro firm.ro store.ro www.ro com.ru net.ru org.ru gov.ru pp.ru com.sa edu.sa sch.sa med.sa gov.sa net.sa org.sa pub.sa com.sb net.sb org.sb edu.sb gov.sb com.sd net.sd org.sd edu.sd sch.sd med.sd gov.sd tm.se press.se parti.se brand.se fh.se fhsk.se fhv.se komforb.se kommunalforbund.se komvux.se lanarb.se lanbib.se naturbruksgymn.se sshn.se org.se pp.se com.sg net.sg org.sg edu.sg gov.sg per.sg com.sh net.sh org.sh edu.sh gov.sh mil.sh gov.st saotome.st principe.st consulado.st embaixada.st org.st edu.st net.st com.st store.st mil.st co.st com.sv org.sv edu.sv gob.sv red.sv com.sy net.sy org.sy gov.sy ac.th co.th go.th net.th or.th com.tn net.tn org.tn edunet.tn gov.tn ens.tn fin.tn nat.tn ind.tn info.tn intl.tn rnrt.tn rnu.tn rns.tn tourism.tn com.tr net.tr org.tr edu.tr gov.tr mil.tr bbs.tr k12.tr gen.tr co.tt com.tt org.tt net.tt biz.tt info.tt pro.tt int.tt coop.tt jobs.tt mobi.tt travel.tt museum.tt aero.tt name.tt gov.tt edu.tt nic.tt us.tt uk.tt ca.tt eu.tt es.tt fr.tt it.tt se.tt dk.tt be.tt de.tt at.tt au.tt co.tv com.tw net.tw org.tw edu.tw idv.tw gov.tw com.ua net.ua org.ua edu.ua gov.ua ac.ug co.ug or.ug go.ug co.uk me.uk org.uk edu.uk ltd.uk plc.uk net.uk sch.uk nic.uk ac.uk gov.uk nhs.uk police.uk mod.uk dni.us fed.us com.uy edu.uy net.uy org.uy gub.uy mil.uy com.ve net.ve org.ve co.ve edu.ve gov.ve mil.ve arts.ve bib.ve firm.ve info.ve int.ve nom.ve rec.ve store.ve tec.ve web.ve co.vi net.vi org.vi com.vn biz.vn edu.vn gov.vn net.vn org.vn int.vn ac.vn pro.vn info.vn health.vn name.vn com.vu edu.vu net.vu org.vu de.vu ch.vu fr.vu com.ws net.ws org.ws gov.ws edu.ws ac.yu co.yu edu.yu org.yu com.ye net.ye org.ye gov.ye edu.ye mil.ye ac.za alt.za bourse.za city.za co.za edu.za gov.za law.za mil.za net.za ngo.za nom.za org.za school.za tm.za web.za co.zw ac.zw org.zw gov.zw eu.org au.com br.com cn.com de.com de.net eu.com gb.com gb.net hu.com no.com qc.com ru.com sa.com se.com uk.com uk.net us.com uy.com za.com dk.org tel.no fax.nr mob.nr mobil.nr mobile.nr tel.nr tlf.nr e164.arpa] 78 | end 79 | --------------------------------------------------------------------------------