commit 7fcc002e02d0f9dcca2cb07a07e18db239252138 Author: Aaron Lindsay Date: Sat Jul 27 01:37:36 2013 -0400 Initial commit diff --git a/specialpass.go b/specialpass.go new file mode 100644 index 0000000..1df9eb8 --- /dev/null +++ b/specialpass.go @@ -0,0 +1,194 @@ +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) + } +}