package apg import ( "crypto/rand" "encoding/binary" "errors" "fmt" "math/big" "strings" ) const ( // 7 bits to represent a letter index letterIdxBits = 7 // All 1-bits, as many as letterIdxBits letterIdxMask = 1<= 0; { if r == 0 { _, err = rand.Read(rp) if err != nil { return rs.String(), err } c, r = binary.BigEndian.Uint64(rp), letterIdxMax } if idx := int(c & letterIdxMask); idx < crl { rs.WriteByte(cr[idx]) i-- } c >>= letterIdxBits r-- } return rs.String(), nil } // RandNum generates a random, non-negative number with given maximum value func (g *Generator) RandNum(m int64) (int64, error) { if m < 1 { return 0, ErrInvalidLength } mbi := big.NewInt(m) rn, err := rand.Int(rand.Reader, mbi) if err != nil { return 0, fmt.Errorf("random number generation failed: %w", err) } return rn.Int64(), nil } // CoinFlip performs a simple coinflip based on the rand library and returns 1 or 0 func (g *Generator) CoinFlip() int64 { cf, _ := g.RandNum(2) return cf } // CoinFlipBool performs a simple coinflip based on the rand library and returns true or false func (g *Generator) CoinFlipBool() bool { return g.CoinFlip() == 1 } // GetPasswordLength returns the password length based on the given config // parameters func (g *Generator) GetPasswordLength() (int64, error) { if g.config.FixedLength > 0 { return g.config.FixedLength, nil } mil := g.config.MinLength mal := g.config.MaxLength if mil > mal { mal = mil } diff := mal - mil + 1 ra, err := g.RandNum(diff) if err != nil { return 0, fmt.Errorf("failed to calculate password length: %w", err) } l := mil + ra if l <= 0 { return 1, nil } return l, nil }