package main import ( "code.google.com/p/gopass" "flag" "fmt" "io" "strings" "crypto/sha512" ) type PasswordRestrictions struct { name string minLength int maxLength int alphaLowerMin int alphaLowerMax int alphaUpperMin int alphaUpperMax int numberMin int numberMax int specialMin int specialMax int allowedSpecial string } var siteName string const DEFAULT_SITE = "etrade" var sites []PasswordRestrictions = []PasswordRestrictions{ PasswordRestrictions{ name: "etrade", minLength: 6, maxLength: 32, alphaLowerMin: 0, alphaLowerMax: 32, alphaUpperMin: 0, alphaUpperMax: 32, numberMin: 1, numberMax: 32, specialMin: 0, specialMax: 0}, PasswordRestrictions{ name: "fidelity", minLength: 6, maxLength: 12, alphaLowerMin: 0, alphaLowerMax: 12, alphaUpperMin: 0, alphaUpperMax: 12, numberMin: 0, numberMax: 12, specialMin: 0, specialMax: 0}, } func init() { const site_usage = "Name of site for which to generate password" flag.StringVar(&siteName, "siteName", DEFAULT_SITE, site_usage) flag.StringVar(&siteName, "s", DEFAULT_SITE, site_usage+" (shorthand)") } func main() { flag.Parse() var restrictions *PasswordRestrictions for _, v := range sites { if strings.ToLower(siteName) == strings.ToLower(v.name) { restrictions = &v break } } if restrictions == nil { fmt.Println("Error: Site name not recognized, please use one of the following:") for _, v := range sites { fmt.Println("\t" + v.name) } } oldPassword, err := gopass.GetPass("Enter Password: ") if err != nil { panic(err) } c := make(chan int) go generateBytes(c, oldPassword, restrictions.name) maxValue := 62 /*alphanumeric*/ + len(restrictions.allowedSpecial) curAlphaLower := 0 curAlphaUpper := 0 curNumber := 0 curSpecial := 0 password := "" lower := "abcdefghijklmnopqrstuvwxyz" upper := "ABCDEFGHIJKLMNOPQRSTUVWXYZ" numbers := "0123456789" //build up the initial string, making sure we don't go over the max allowed for each type for length := 0; length < restrictions.maxLength; length++ { val := <- c % maxValue switch { case val >= 0 && val < 26: if curAlphaLower + 1 <= restrictions.alphaLowerMax { password += lower[val:val+1] curAlphaLower++ } case val >= 26 && val < 52: if curAlphaUpper + 1 <= restrictions.alphaUpperMax { password += upper[val-26:val-25] curAlphaUpper++ } case val >= 52 && val < 62: if curNumber + 1 <= restrictions.numberMax { password += numbers[val-52:val-51] curNumber++ } default: if curSpecial + 1 <= restrictions.specialMax { password += restrictions.allowedSpecial[val-62:val-61] curSpecial++ } } } //now, make sure we meet the minimum requirements. If we don't, start replacing items in the password for { removed := true switch { case curAlphaLower < restrictions.alphaLowerMin: val := <- c % 26 password += lower[val:val+1] curAlphaLower++ case curAlphaUpper < restrictions.alphaUpperMin: val := <- c % 26 password += upper[val:val+1] curAlphaUpper++ case curNumber < restrictions.numberMin: val := <- c % 10 password += numbers[val:val+1] curNumber++ case curSpecial < restrictions.specialMin: val := <- c % len(restrictions.allowedSpecial) password += restrictions.allowedSpecial[val:val+1] curSpecial++ default: removed = false } if !removed { break } removedChar := password[:1] password = password[1:] switch{ case strings.Contains(lower, removedChar): curAlphaLower-- case strings.Contains(upper, removedChar): curAlphaUpper-- case strings.Contains(numbers, removedChar): curNumber-- case strings.Contains(restrictions.allowedSpecial, removedChar): curSpecial-- default: panic("Unknown character made its way into the password") } } //finally, (pseudo-)randomize the password ordering, one element at a time for i := 0; i < restrictions.maxLength; i++ { position := (<- c % (restrictions.maxLength - i)) + i password = password[position:position+1] + password[0:position] + password[position+1:restrictions.maxLength] } fmt.Println("Generated password for " + restrictions.name + ": " + password) } func generateBytes(c chan int, password string, siteName string) { h := sha512.New() io.WriteString(h, password + strings.ToLower(siteName)) hashBytes := h.Sum(nil) for { for _, b := range hashBytes { c <- int(b) } h.Write(hashBytes) hashBytes = h.Sum(nil) } }