|  | @@ -0,0 +1,242 @@
 | 
	
		
			
				|  |  | +// Package password provides a library for generating high-entropy random
 | 
	
		
			
				|  |  | +// password strings via the crypto/rand package.
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +//    res, err := Generate(64, 10, 10, false, false)
 | 
	
		
			
				|  |  | +//    if err != nil  {
 | 
	
		
			
				|  |  | +//      log.Fatal(err)
 | 
	
		
			
				|  |  | +//    }
 | 
	
		
			
				|  |  | +//    log.Printf(res)
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +// Most functions are safe for concurrent use.
 | 
	
		
			
				|  |  | +package password
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +import (
 | 
	
		
			
				|  |  | +	"crypto/rand"
 | 
	
		
			
				|  |  | +	"errors"
 | 
	
		
			
				|  |  | +	"math/big"
 | 
	
		
			
				|  |  | +	"strings"
 | 
	
		
			
				|  |  | +)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +const (
 | 
	
		
			
				|  |  | +	// LowerLetters is the list of lowercase letters.
 | 
	
		
			
				|  |  | +	LowerLetters = "abcdefghijklmnopqrstuvwxyz"
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	// UpperLetters is the list of uppercase letters.
 | 
	
		
			
				|  |  | +	UpperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	// Digits is the list of permitted digits.
 | 
	
		
			
				|  |  | +	Digits = "0123456789"
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	// Symbols is the list of symbols.
 | 
	
		
			
				|  |  | +	Symbols = "~!@#$%^&*()_+`-={}|[]\\:\"<>?,./"
 | 
	
		
			
				|  |  | +)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +var (
 | 
	
		
			
				|  |  | +	// ErrExceedsTotalLength is the error returned with the number of digits and
 | 
	
		
			
				|  |  | +	// symbols is greater than the total length.
 | 
	
		
			
				|  |  | +	ErrExceedsTotalLength = errors.New("number of digits and symbols must be less than total length")
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	// ErrLettersExceedsAvailable is the error returned with the number of letters
 | 
	
		
			
				|  |  | +	// exceeds the number of available letters and repeats are not allowed.
 | 
	
		
			
				|  |  | +	ErrLettersExceedsAvailable = errors.New("number of letters exceeds available letters and repeats are not allowed")
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	// ErrDigitsExceedsAvailable is the error returned with the number of digits
 | 
	
		
			
				|  |  | +	// exceeds the number of available digits and repeats are not allowed.
 | 
	
		
			
				|  |  | +	ErrDigitsExceedsAvailable = errors.New("number of digits exceeds available digits and repeats are not allowed")
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	// ErrSymbolsExceedsAvailable is the error returned with the number of symbols
 | 
	
		
			
				|  |  | +	// exceeds the number of available symbols and repeats are not allowed.
 | 
	
		
			
				|  |  | +	ErrSymbolsExceedsAvailable = errors.New("number of symbols exceeds available symbols and repeats are not allowed")
 | 
	
		
			
				|  |  | +)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Generator is the stateful generator which can be used to customize the list
 | 
	
		
			
				|  |  | +// of letters, digits, and/or symbols.
 | 
	
		
			
				|  |  | +type Generator struct {
 | 
	
		
			
				|  |  | +	lowerLetters string
 | 
	
		
			
				|  |  | +	upperLetters string
 | 
	
		
			
				|  |  | +	digits       string
 | 
	
		
			
				|  |  | +	symbols      string
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// GeneratorInput is used as input to the NewGenerator function.
 | 
	
		
			
				|  |  | +type GeneratorInput struct {
 | 
	
		
			
				|  |  | +	LowerLetters string
 | 
	
		
			
				|  |  | +	UpperLetters string
 | 
	
		
			
				|  |  | +	Digits       string
 | 
	
		
			
				|  |  | +	Symbols      string
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// NewGenerator creates a new Generator from the specified configuration. If no
 | 
	
		
			
				|  |  | +// input is given, all the default values are used. This function is safe for
 | 
	
		
			
				|  |  | +// concurrent use.
 | 
	
		
			
				|  |  | +func NewGenerator(i *GeneratorInput) (*Generator, error) {
 | 
	
		
			
				|  |  | +	if i == nil {
 | 
	
		
			
				|  |  | +		i = new(GeneratorInput)
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	g := &Generator{
 | 
	
		
			
				|  |  | +		lowerLetters: i.LowerLetters,
 | 
	
		
			
				|  |  | +		upperLetters: i.UpperLetters,
 | 
	
		
			
				|  |  | +		digits:       i.Digits,
 | 
	
		
			
				|  |  | +		symbols:      i.Symbols,
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if g.lowerLetters == "" {
 | 
	
		
			
				|  |  | +		g.lowerLetters = LowerLetters
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if g.upperLetters == "" {
 | 
	
		
			
				|  |  | +		g.upperLetters = UpperLetters
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if g.digits == "" {
 | 
	
		
			
				|  |  | +		g.digits = Digits
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if g.symbols == "" {
 | 
	
		
			
				|  |  | +		g.symbols = Symbols
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return g, nil
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Generate generates a password with the given requirements. length is the
 | 
	
		
			
				|  |  | +// total number of characters in the password. numDigits is the number of digits
 | 
	
		
			
				|  |  | +// to include in the result. numSymbols is the number of symbols to include in
 | 
	
		
			
				|  |  | +// the result. noUpper excludes uppercase letters from the results. allowRepeat
 | 
	
		
			
				|  |  | +// allows characters to repeat.
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +// The algorithm is fast, but it's not designed to be performant; it favors
 | 
	
		
			
				|  |  | +// entropy over speed. This function is safe for concurrent use.
 | 
	
		
			
				|  |  | +func (g *Generator) Generate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) (string, error) {
 | 
	
		
			
				|  |  | +	letters := g.lowerLetters
 | 
	
		
			
				|  |  | +	if !noUpper {
 | 
	
		
			
				|  |  | +		letters += g.upperLetters
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	chars := length - numDigits - numSymbols
 | 
	
		
			
				|  |  | +	if chars < 0 {
 | 
	
		
			
				|  |  | +		return "", ErrExceedsTotalLength
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if !allowRepeat && chars > len(letters) {
 | 
	
		
			
				|  |  | +		return "", ErrLettersExceedsAvailable
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if !allowRepeat && numDigits > len(g.digits) {
 | 
	
		
			
				|  |  | +		return "", ErrDigitsExceedsAvailable
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if !allowRepeat && numSymbols > len(g.symbols) {
 | 
	
		
			
				|  |  | +		return "", ErrSymbolsExceedsAvailable
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	var result string
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	// Characters
 | 
	
		
			
				|  |  | +	for i := 0; i < chars; i++ {
 | 
	
		
			
				|  |  | +		ch, err := randomElement(letters)
 | 
	
		
			
				|  |  | +		if err != nil {
 | 
	
		
			
				|  |  | +			return "", err
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		if !allowRepeat && strings.Contains(result, ch) {
 | 
	
		
			
				|  |  | +			i--
 | 
	
		
			
				|  |  | +			continue
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		result, err = randomInsert(result, ch)
 | 
	
		
			
				|  |  | +		if err != nil {
 | 
	
		
			
				|  |  | +			return "", err
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	// Digits
 | 
	
		
			
				|  |  | +	for i := 0; i < numDigits; i++ {
 | 
	
		
			
				|  |  | +		d, err := randomElement(g.digits)
 | 
	
		
			
				|  |  | +		if err != nil {
 | 
	
		
			
				|  |  | +			return "", err
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		if !allowRepeat && strings.Contains(result, d) {
 | 
	
		
			
				|  |  | +			i--
 | 
	
		
			
				|  |  | +			continue
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		result, err = randomInsert(result, d)
 | 
	
		
			
				|  |  | +		if err != nil {
 | 
	
		
			
				|  |  | +			return "", err
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	// Symbols
 | 
	
		
			
				|  |  | +	for i := 0; i < numSymbols; i++ {
 | 
	
		
			
				|  |  | +		sym, err := randomElement(g.symbols)
 | 
	
		
			
				|  |  | +		if err != nil {
 | 
	
		
			
				|  |  | +			return "", err
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		if !allowRepeat && strings.Contains(result, sym) {
 | 
	
		
			
				|  |  | +			i--
 | 
	
		
			
				|  |  | +			continue
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		result, err = randomInsert(result, sym)
 | 
	
		
			
				|  |  | +		if err != nil {
 | 
	
		
			
				|  |  | +			return "", err
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return result, nil
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// MustGenerate is the same as Generate, but panics on error.
 | 
	
		
			
				|  |  | +func (g *Generator) MustGenerate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) string {
 | 
	
		
			
				|  |  | +	res, err := g.Generate(length, numDigits, numSymbols, noUpper, allowRepeat)
 | 
	
		
			
				|  |  | +	if err != nil {
 | 
	
		
			
				|  |  | +		panic(err)
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	return res
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// See Generator.Generate for usage.
 | 
	
		
			
				|  |  | +func Generate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) (string, error) {
 | 
	
		
			
				|  |  | +	gen, err := NewGenerator(nil)
 | 
	
		
			
				|  |  | +	if err != nil {
 | 
	
		
			
				|  |  | +		return "", err
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return gen.Generate(length, numDigits, numSymbols, noUpper, allowRepeat)
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// See Generator.MustGenerate for usage.
 | 
	
		
			
				|  |  | +func MustGenerate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) string {
 | 
	
		
			
				|  |  | +	res, err := Generate(length, numDigits, numSymbols, noUpper, allowRepeat)
 | 
	
		
			
				|  |  | +	if err != nil {
 | 
	
		
			
				|  |  | +		panic(err)
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	return res
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// randomInsert randomly inserts the given value into the given string.
 | 
	
		
			
				|  |  | +func randomInsert(s, val string) (string, error) {
 | 
	
		
			
				|  |  | +	if s == "" {
 | 
	
		
			
				|  |  | +		return val, nil
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	n, err := rand.Int(rand.Reader, big.NewInt(int64(len(s))))
 | 
	
		
			
				|  |  | +	if err != nil {
 | 
	
		
			
				|  |  | +		return "", err
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	i := n.Int64()
 | 
	
		
			
				|  |  | +	return s[0:i] + val + s[i:len(s)], nil
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// randomElement extracts a random element from the given string.
 | 
	
		
			
				|  |  | +func randomElement(s string) (string, error) {
 | 
	
		
			
				|  |  | +	n, err := rand.Int(rand.Reader, big.NewInt(int64(len(s))))
 | 
	
		
			
				|  |  | +	if err != nil {
 | 
	
		
			
				|  |  | +		return "", err
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	return string(s[n.Int64()]), nil
 | 
	
		
			
				|  |  | +}
 |