NEM 2008-11-27: Here's a Tk version of the Site Password program by Alan Karp at HP Labs [L1 ]. Instead of storing lots of passwords for every website you visit, or using the same password everywhere, this program lets you calculate a password based on a master password and an easy to remember site name. It works by concatenating the master password and the site name, taking the MD5 hash of these and base64 encoding the result. It works quite nicely. The version here improves on the Python version on the site by using Ttk widgets and allowing you to save previously entered site names. This version also support a command-line only mode: simply pass the sitename as an argument:
sitepw.tcl ?sitename ?-alphanumeric 0|1??
(Note: command-line only works on UNIX-like systems that have stty).
Simply type in your master password and then enter the website name. The site-specific password will appear in the Site Password box. You can copy this password to the clipboard using the Copy button (or select and Ctrl-C as usual). The Add button saves the current website name to a preferences file (don't worry -- the site name is not sufficient to calculate your passwords). On UNIX this prefs file is stored in ~/.sitepw, on Windows in ~/Application Data/sitepw.rc (may need to be localised?), and on Mac in ~/Library/Preferences/sitepw.rc. The Done button exits the application.
#!/usr/bin/env tclsh # sitepw.tcl package require Tcl 8.5 package require md5 2.0 package require base64 2.3 proc sitepw args { if {[llength $args] >= 1} { set pw [readpw] puts [password $pw {*}$args] } else { package require Tk 8.5 CreateGUI } } proc readpw {} { # Read password from stdin in a safe-ish manner puts -nonewline "Password: " flush stdout exec stty -echo gets stdin pw exec stty echo return $pw } # Compute a site-specific password proc password {master sitename args} { array set ops { -alphanumeric 0 -length 12 } array set ops $args # Compute MD5 hash of inputs to generate unique password set sitename [string tolower $sitename] set digest [md5::md5 $sitename$master] set digest [base64::encode $digest] if {$ops(-alphanumeric)} { set digest [string map {/ "" + ""} $digest] } return [string range $digest 0 [expr {$ops(-length)-1}]] } proc CreateGUI {} { set f [ttk::frame .f] ttk::label $f.pl -text "Master Password:" -anchor e ttk::entry $f.passwd -textvariable ::master -show * ttk::label $f.sl -text "Website:" -anchor e ttk::combobox $f.site -textvariable ::site ttk::label $f.gl -text "Site Password:" -anchor e ttk::entry $f.spass -textvariable ::sitepw ttk::checkbutton $f.alpha -text "Letters/numbers only" -variable ::alpha set tb [ttk::frame .tb] ttk::button $tb.done -text "Done" -command Exit ttk::button $tb.save -text "Add" -command [list AddSite $f.site] ttk::button $tb.copy -text "Copy" \ -command [list Copy $f.spass] bind . <Return> [list $tb.copy invoke] set ::master "" set ::site "" set ::sitepw "" set ::alpha 0 LoadSites $f.site wm protocol . WM_DELETE_WINDOW Exit grid $f.pl $f.passwd -sticky ew grid $f.sl $f.site -sticky ew grid $f.gl $f.spass -sticky ew grid $f.alpha - grid columnconfigure $f 1 -weight 1 grid $tb.done $tb.save $tb.copy -sticky e -padx 10 -pady 2 grid $f -sticky ew grid $tb -sticky ew grid columnconfigure . 0 -weight 1 wm resizable . 1 0 wm title . "Site Password" trace add variable ::master write UpdatePW trace add variable ::site write UpdatePW trace add variable ::alpha write UpdatePW } # Load list of previously accessed sites into the combobox w proc LoadSites w { catch { set in [open [PrefFile] r] set sites [split [string trim [read $in]] \n] close $in $w configure -values $sites } } proc SaveSites w { set out [open [PrefFile] w] puts $out [join [$w cget -values] \n] close $out } proc Exit {} { SaveSites .f.site exit } proc AddSite w { set sites [$w cget -values] set site [$w get] lappend sites $site set sites [lsort -unique -dictionary $sites] $w configure -values $sites } proc PrefFile {} { switch -exact [tk windowingsystem] { x11 { return [file normalize ~/.sitepw] } win { return [file normalize "~/Application Data/sitepw.rc"] } aqua { return [file normalize ~/Library/Preferences/sitepw.rc] } } } proc Copy w { clipboard clear clipboard append [$w get] } proc UpdatePW {args} { global master site sitepw alpha set sitepw [password $master $site -alphanumeric $alpha] } sitepw {*}$argv
See also: Password Gorilla