From 2822f73f5603e7030af01facee95d7aa5ab2f33e Mon Sep 17 00:00:00 2001 From: Winni Neessen Date: Sat, 5 Aug 2023 18:10:11 +0200 Subject: [PATCH] #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. --- algo.go | 5 +++++ cmd/apg/apg.go | 17 ++++++++++++++--- random.go | 24 +++++++++++++++++++++++- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/algo.go b/algo.go index 5cfc5f4..63fb0db 100644 --- a/algo.go +++ b/algo.go @@ -11,6 +11,9 @@ const ( // AlgoRandom represents the algorithm for purely random passwords according // to the provided password modes/flags AlgoRandom + // AlgoCoinFlip represents a very simple coinflip algorithm returning "heads" + // or "tails" + AlgoCoinFlip // AlgoUnsupported represents an unsupported algorithm AlgoUnsupported ) @@ -23,6 +26,8 @@ func IntToAlgo(a int) Algorithm { return AlgoPronouncable case 1: return AlgoRandom + case 2: + return AlgoCoinFlip default: return AlgoUnsupported } diff --git a/cmd/apg/apg.go b/cmd/apg/apg.go index 2e5b0ba..641f3a3 100644 --- a/cmd/apg/apg.go +++ b/cmd/apg/apg.go @@ -15,8 +15,10 @@ func main() { // Configure and parse the CLI flags // See usage() for flag details + var al int var ms string var co, hr, lc, nu, sp, uc bool + flag.IntVar(&al, "a", 1, "") flag.BoolVar(&lc, "L", false, "") flag.Int64Var(&c.MinLowerCase, "mL", c.MinLowerCase, "") flag.BoolVar(&uc, "U", false, "") @@ -62,14 +64,22 @@ func main() { 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 g := apg.New(c) for i := int64(0); i < c.NumberPass; i++ { - pl, err := g.GetPasswordLength() + p, err := g.Generate() 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) - 0: pronounceable password generation (koremutake syllables) - 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) -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) diff --git a/random.go b/random.go index 5b653d7..83c995b 100644 --- a/random.go +++ b/random.go @@ -27,6 +27,28 @@ var ( 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 // the crypto/rand generator func (g *Generator) RandomBytes(n int64) ([]byte, error) { @@ -121,7 +143,7 @@ func (g *Generator) GetPasswordLength() (int64, error) { diff := mal - mil + 1 ra, err := g.RandNum(diff) if err != nil { - return 0, fmt.Errorf("failed to calculate password length: %w", err) + return 0, err } l := mil + ra if l <= 0 {