diff --git a/passgen/passgen.go b/passgen/passgen.go index af19f55..4efe0d9 100644 --- a/passgen/passgen.go +++ b/passgen/passgen.go @@ -1,62 +1,62 @@ -// Package passgen generates random, unmemorable passwords. +// Package passgen generates random passwords. // // Example usage: // // p := passgen.New() // p is "6NX(W`GD]4:Tqk};Y@A-" // // Logic originally from dchest's uniuri library: // https://github.com/dchest/uniuri // // Functions read from crypto/rand random source, and panic if they fail to // read from it. package passgen import "crypto/rand" // DefLen is the default password length returned. const DefLen = 20 // DefChars is the default set of characters used in the password. var DefChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()- _=+,.?/:;{}[]`~") -// New returns a random password of the default length with the default set of -// characters. +// New returns a random, unmemorable password of the default length with the +// default set of characters. func New() string { return NewLenChars(DefLen, DefChars) } -// NewLen returns a random password of the given length with the default set -// of characters. +// NewLen returns a random, unmemorable password of the given length with the +// default set of characters. func NewLen(length int) string { return NewLenChars(length, DefChars) } -// NewLenChars returns a random password of the given length with the given -// set of characters. +// NewLenChars returns a random, unmemorable password of the given length with +// the given set of characters. func NewLenChars(length int, chars []byte) string { if length == 0 { return "" } clen := len(chars) maxrb := 255 - (256 % clen) b := make([]byte, length) r := make([]byte, length+(length/4)) // storage for random bytes. i := 0 for { if _, err := rand.Read(r); err != nil { panic("passgen: error reading random bytes: " + err.Error()) } for _, rb := range r { c := int(rb) if c > maxrb { // Skip this number to avoid modulo bias. continue } b[i] = chars[c%clen] i++ if i == length { return string(b) } } } } diff --git a/passgen/wordish.go b/passgen/wordish.go new file mode 100644 index 0000000..4749a56 --- /dev/null +++ b/passgen/wordish.go @@ -0,0 +1,64 @@ +package passgen + +import ( + "crypto/rand" + "math/big" +) + +var ( + ar = []rune("aA4") + cr = []rune("cC") + er = []rune("eE3") + fr = []rune("fF") + gr = []rune("gG") + hr = []rune("hH") + ir = []rune("iI1") + lr = []rune("lL") + nr = []rune("nN") + or = []rune("oO0") + rr = []rune("rR") + sr = []rune("sS5") + tr = []rune("tT7") + remr = []rune("bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ0123456789") +) + +// NewWordish generates a password made of word-like words. +func NewWordish() string { + b := []rune{} + b = append(b, randLetter(cr)) + b = append(b, randLetter(hr)) + b = append(b, randLetter(ar)) + b = append(b, randLetter(nr)) + b = append(b, randLetter(gr)) + b = append(b, randLetter(er)) + b = append(b, randLetter(tr)) + b = append(b, randLetter(hr)) + b = append(b, randLetter(ir)) + b = append(b, randLetter(sr)) + b = append(b, randLetter(ar)) + b = append(b, randLetter(fr)) + b = append(b, randLetter(tr)) + b = append(b, randLetter(er)) + b = append(b, randLetter(rr)) + b = append(b, randLetter(lr)) + b = append(b, randLetter(or)) + b = append(b, randLetter(gr)) + b = append(b, randLetter(gr)) + b = append(b, randLetter(ir)) + b = append(b, randLetter(nr)) + b = append(b, randLetter(gr)) + b = append(b, randLetter(ir)) + b = append(b, randLetter(nr)) + for i := 0; i <= 7; i++ { + b = append(b, randLetter(remr)) + } + return string(b) +} + +func randLetter(l []rune) rune { + li, err := rand.Int(rand.Reader, big.NewInt(int64(len(l)))) + if err != nil { + return rune(-1) + } + return l[li.Int64()] +}