mirror of
https://github.com/wneessen/apg-go.git
synced 2024-11-24 23:00:50 +01:00
Winni Neessen
ac97b94ec9
Refactored the generator to include a new config option, changed function signatures to follow the new structure, and renamed the function 'RandomString' to 'RandomStringFromCharRange' for clarity. Also, added a new mode and algorithm feature to enhance password generation. Furthermore, added several tests for new features and configurations. Adapted the CLI to use the new configuration approach. This refactoring was necessary to improve the customizability and clarity of the password generation process. Fixed minor issues and added '.gitignore' for clean commits in the future.
108 lines
2.8 KiB
Go
108 lines
2.8 KiB
Go
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<<letterIdxBits - 1
|
|
// # of letter indices fitting in 63 bits)
|
|
letterIdxMax = 63 / letterIdxBits
|
|
)
|
|
|
|
var (
|
|
// ErrInvalidLength is returned if the provided maximum number is equal or less than zero
|
|
ErrInvalidLength = errors.New("provided length value cannot be zero or less")
|
|
// ErrLengthMismatch is returned if the number of generated bytes does not match the expected length
|
|
ErrLengthMismatch = errors.New("number of generated random bytes does not match the expected length")
|
|
// ErrInvalidCharRange is returned if the given range of characters is not valid
|
|
ErrInvalidCharRange = errors.New("provided character range is not valid or empty")
|
|
)
|
|
|
|
// RandomBytes returns a byte slice of random bytes with length n that got generated by
|
|
// the crypto/rand generator
|
|
func (g *Generator) RandomBytes(n int64) ([]byte, error) {
|
|
if n < 1 {
|
|
return nil, ErrInvalidLength
|
|
}
|
|
b := make([]byte, n)
|
|
l, err := rand.Read(b)
|
|
if int64(l) != n {
|
|
return nil, ErrLengthMismatch
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return b, nil
|
|
}
|
|
|
|
// RandomStringFromCharRange returns a random string of length l based of the range of characters given.
|
|
// The method makes use of the crypto/random package and therfore is
|
|
// cryptographically secure
|
|
func (g *Generator) RandomStringFromCharRange(l int, cr string) (string, error) {
|
|
if l < 1 {
|
|
return "", ErrInvalidLength
|
|
}
|
|
if len(cr) < 1 {
|
|
return "", ErrInvalidCharRange
|
|
}
|
|
rs := strings.Builder{}
|
|
rs.Grow(l)
|
|
crl := len(cr)
|
|
|
|
rp := make([]byte, 8)
|
|
_, err := rand.Read(rp)
|
|
if err != nil {
|
|
return rs.String(), err
|
|
}
|
|
for i, c, r := l-1, binary.BigEndian.Uint64(rp), letterIdxMax; i >= 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
|
|
}
|