package pwgenerator import ( "strings" "r00t2.io/goutils/multierr" ) // Generate generates a list of passwords for a GenOpts. func (o *GenOpts) Generate() (passwords []string, err error) { var passwds []string = make([]string, o.Count) var charset CharSet = o.Chars() var errs *multierr.MultiError = multierr.NewMultiError(nil) if o.Count == 0 { o.Count = DefCount } if o.LengthMax == 0 { o.LengthMax = DefMaxLen } for idx, _ := range passwds { if passwds[idx], err = o.generatePassword(charset); err != nil { errs.AddError(err) err = nil } } passwords = passwds if !errs.IsEmpty() { err = errs } return } // generatePassword generates a single password from CharSet c. func (o *GenOpts) generatePassword(c CharSet) (password string, err error) { var maxMin uint var trueMinLen uint var passLenGap uint var passLen int var passAlloc []rune // Sanity checks/error conditions. maxMin = o.CountUpper + o.CountLower + o.CountSymbols + o.CountExtended if maxMin > o.LengthMax { err = ErrTooSmall return } if o.LengthMin > o.LengthMax { err = ErrSwitchedLenLimits return } // Defaults. trueMinLen = o.LengthMin if trueMinLen == 0 { trueMinLen = DefMinLin } // Get a fixed password length... passLenGap = o.LengthMax - trueMinLen if passLen, err = saferRandInt(int(passLenGap)); err != nil { return } passLen = passLen + int(trueMinLen) // (We need to re-add; it was subtracted above to get a zero-shifted number for start in saferRandInt.) // And make the rune slice of that length. passAlloc = make([]rune, passLen) for idx, _ := range passAlloc { var char Char for { var isDisabled bool if char, err = c.RandChar(); err != nil { return } if isDisabled, err = o.DisabledChars.Has(char); err != nil || isDisabled { err = nil continue } passAlloc[idx] = rune(char) break } } password = string(passAlloc) return } // Chars returns the list of evaluated characters that would be used in a GenOpts. func (o *GenOpts) Chars() (chars CharSet) { chars = make(CharSet, 0) if o.explicitCharset != nil && len(o.explicitCharset) != 0 { chars = o.explicitCharset return } if o.Alpha { chars = append(chars, alpha...) } if o.Numeric { chars = append(chars, numeric...) } if o.Symbols { chars = append(chars, symbols...) } if o.ExtendedSymbols { chars = append(chars, extendedSymbols...) } // TODO: Count* fields return } /* SetExplicitCharset sets a GenOpts' charset to an explicit selection. It is deduplicated before being used unless inclDupes is true. (Duplicate runes can be used to increase the likelihood of specific characters.) Note that if an explicit charset is defined, the following opts will have NO effect: Alpha Numeric Symbols ExtendedSymbols CountUpper CountLower CountSymbols CountExtended DisabledChars To have these fields be used again, call GenOpts.UnsetExplicitCharset. chars can be a CharSet, []rune, []byte, []string, or string */ func (o *GenOpts) SetExplicitCharset(chars interface{}, inclDupes bool) (err error) { var charset CharSet switch t := chars.(type) { case []rune: charset = CharSet(string(t)) case []byte: charset = CharSet(string(t)) case []string: s := strings.Join(t, "") charset = CharSet(s) case string: charset = CharSet(t) default: err = ErrBadType return } if !inclDupes { sortDedupe(&charset) } o.explicitCharset = charset return } /* UnsetExplicitCharset removes the explicit charset used for generation and instead uses the fields specified in the actual GenOpts. See GenOpts.SetExplicitCharset for more details. */ func (o *GenOpts) UnsetExplicitCharset() { o.explicitCharset = nil return }