#53 Add coinflip algorithm and improve error messages

Introduced a new password generation algorithm, called 'coinflip', which simply returns "Heads" or "Tails". Associated CLI flag has been added as well. Also, improved error messages during password generation. This addition provides a simpler algorithm option and clearer user feedback during errors.
This commit is contained in:
Winni Neessen 2023-08-05 18:10:11 +02:00
parent 8d42651e58
commit 2822f73f56
Signed by: wneessen
GPG key ID: 5F3AF39B820C119D
3 changed files with 42 additions and 4 deletions

View file

@ -11,6 +11,9 @@ const (
// AlgoRandom represents the algorithm for purely random passwords according // AlgoRandom represents the algorithm for purely random passwords according
// to the provided password modes/flags // to the provided password modes/flags
AlgoRandom AlgoRandom
// AlgoCoinFlip represents a very simple coinflip algorithm returning "heads"
// or "tails"
AlgoCoinFlip
// AlgoUnsupported represents an unsupported algorithm // AlgoUnsupported represents an unsupported algorithm
AlgoUnsupported AlgoUnsupported
) )
@ -23,6 +26,8 @@ func IntToAlgo(a int) Algorithm {
return AlgoPronouncable return AlgoPronouncable
case 1: case 1:
return AlgoRandom return AlgoRandom
case 2:
return AlgoCoinFlip
default: default:
return AlgoUnsupported return AlgoUnsupported
} }

View file

@ -15,8 +15,10 @@ func main() {
// Configure and parse the CLI flags // Configure and parse the CLI flags
// See usage() for flag details // See usage() for flag details
var al int
var ms string var ms string
var co, hr, lc, nu, sp, uc bool var co, hr, lc, nu, sp, uc bool
flag.IntVar(&al, "a", 1, "")
flag.BoolVar(&lc, "L", false, "") flag.BoolVar(&lc, "L", false, "")
flag.Int64Var(&c.MinLowerCase, "mL", c.MinLowerCase, "") flag.Int64Var(&c.MinLowerCase, "mL", c.MinLowerCase, "")
flag.BoolVar(&uc, "U", false, "") flag.BoolVar(&uc, "U", false, "")
@ -62,14 +64,22 @@ func main() {
c.Mode = apg.ModesFromFlags(ms) c.Mode = apg.ModesFromFlags(ms)
} }
// Check if algorithm is supported
c.Algorithm = apg.IntToAlgo(al)
if c.Algorithm == apg.AlgoUnsupported {
_, _ = fmt.Fprintf(os.Stderr, "unsupported algorithm value: %d\n", al)
os.Exit(1)
}
// Generate the password based on the given flags // Generate the password based on the given flags
g := apg.New(c) g := apg.New(c)
for i := int64(0); i < c.NumberPass; i++ { for i := int64(0); i < c.NumberPass; i++ {
pl, err := g.GetPasswordLength() p, err := g.Generate()
if err != nil { if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Error during password generation: %s\n", err) _, _ = fmt.Fprintf(os.Stderr, "failed to generate password: %s\n", err)
os.Exit(1)
} }
fmt.Printf("PW length: %d\n", pl) fmt.Println(p)
} }
} }
@ -88,6 +98,7 @@ Flags:
-a ALGORITH Choose the password generation algorithm (Default: 1) -a ALGORITH Choose the password generation algorithm (Default: 1)
- 0: pronounceable password generation (koremutake syllables) - 0: pronounceable password generation (koremutake syllables)
- 1: random password generation according to password modes/flags - 1: random password generation according to password modes/flags
- 2: coinflip (returns heads or tails)
-m LENGTH Minimum length of the password to be generated (Default: 12) -m LENGTH Minimum length of the password to be generated (Default: 12)
-x LENGTH Maximum length of the password to be generated (Default: 20) -x LENGTH Maximum length of the password to be generated (Default: 20)
-f LENGTH Fixed length of the password to be generated (Ignores -m and -x) -f LENGTH Fixed length of the password to be generated (Ignores -m and -x)

View file

@ -27,6 +27,28 @@ var (
ErrInvalidCharRange = errors.New("provided character range is not valid or empty") ErrInvalidCharRange = errors.New("provided character range is not valid or empty")
) )
// Generate generates a password based on all the different config flags and returns
// it as string type. If the generation fails, an error will be thrown
func (g *Generator) Generate() (string, error) {
// Coinflip mode
if g.config.Algorithm == AlgoCoinFlip {
switch g.CoinFlipBool() {
case true:
return "Heads", nil
case false:
return "Tails", nil
}
}
l, err := g.GetPasswordLength()
if err != nil {
return "", fmt.Errorf("failed to calculate password length: %w", err)
}
_ = l
return "", nil
}
// RandomBytes returns a byte slice of random bytes with length n that got generated by // RandomBytes returns a byte slice of random bytes with length n that got generated by
// the crypto/rand generator // the crypto/rand generator
func (g *Generator) RandomBytes(n int64) ([]byte, error) { func (g *Generator) RandomBytes(n int64) ([]byte, error) {
@ -121,7 +143,7 @@ func (g *Generator) GetPasswordLength() (int64, error) {
diff := mal - mil + 1 diff := mal - mil + 1
ra, err := g.RandNum(diff) ra, err := g.RandNum(diff)
if err != nil { if err != nil {
return 0, fmt.Errorf("failed to calculate password length: %w", err) return 0, err
} }
l := mil + ra l := mil + ra
if l <= 0 { if l <= 0 {