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 {*}$argvSee also: Password Gorilla