195 lines
4.5 KiB
Go
195 lines
4.5 KiB
Go
|
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)
|
||
|
}
|
||
|
}
|