From 1255ff8b48b3f1b7ea75c4114986116e5743034b Mon Sep 17 00:00:00 2001 From: Amolith Date: Tue, 8 Oct 2024 14:40:01 -0600 Subject: [PATCH] Simplify, switch to correct crypto lib --- go.mod | 2 + go.sum | 2 + main.go | 115 ++++++++++++++++++-------------------------------------- 3 files changed, 41 insertions(+), 78 deletions(-) create mode 100644 go.sum diff --git a/go.mod b/go.mod index f950e3187d9bf33a74de24aee66750f91d4d2e0c..80e9623043204966ea7ea2060b7228339cfa71a5 100644 --- a/go.mod +++ b/go.mod @@ -5,3 +5,5 @@ module git.sr.ht/~amolith/eow go 1.23.1 + +require github.com/spf13/pflag v1.0.5 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..287f6fa8e2417922229405abc2669fe7b8fd93e0 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= diff --git a/main.go b/main.go index 0c4570a6dcbfaac6f15160e5f9fd5e99c7055f63..54fe4d7dfca04aff986985941fef4d66b83e3150 100644 --- a/main.go +++ b/main.go @@ -5,136 +5,95 @@ package main import ( + "crypto/rand" "fmt" - "math/rand" + "math/big" "os" - "strconv" "strings" - "time" + + flag "github.com/spf13/pflag" ) var ( - length = 20 version = "" consonants = "bcdfghjklmnpqrstvwxyz" vowels = "aeiou" numbers = "0123456789" newBase60Chars = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ_abcdefghijkmnopqrstuvwxyz" + flagNB60 = flag.BoolP("nb60", "n", false, "Generate a 20-character password using the NewBase60 charset") + flagFriendly = flag.BoolP("friendly", "f", true, "Generate a 20-character password using an Apple-style friendly algorithm") ) func main() { - if len(os.Args) > 1 && os.Args[1] == "-h" { - printHelp() - os.Exit(0) - } else if len(os.Args) > 1 && os.Args[1] == "-t" { - switch os.Args[2] { - case "nb60": - fmt.Println(generate(length)) - case "friendly": - fmt.Println(generateFriendly()) - } - } else if len(os.Args) > 3 { - fmt.Print("Error: too many arguments...\n\n") - printHelp() - os.Exit(1) - } else if len(os.Args) == 2 { - // Convert the string argument to an int - length, err := strconv.Atoi(os.Args[1]) - if err != nil { - fmt.Println("Error: length must be an integer") - os.Exit(1) - } - fmt.Println(generate(length)) - os.Exit(0) - } else if len(os.Args) == 3 { - length, err := strconv.Atoi(os.Args[1]) - if err != nil { - fmt.Println("Error: length must be an integer") - os.Exit(1) - } - count, err := strconv.Atoi(os.Args[2]) - if err != nil { - fmt.Println("Error: count must be an integer") - os.Exit(1) - } - for i := 0; i < count; i++ { - fmt.Println(generate(length)) - } - os.Exit(0) - } else if len(os.Args) == 1 { - fmt.Println(generate(length)) + flag.Parse() + if *flagNB60 { + fmt.Println(generateNB60()) os.Exit(0) } + fmt.Println(generateFriendly()) } -func generate(length int) string { - rng := rand.New(rand.NewSource(time.Now().UnixNano())) - +func generateNB60() string { var sb strings.Builder - sb.Grow(length) + sb.Grow(20) - for i := 0; i < length; i++ { - sb.WriteByte(newBase60Chars[rng.Intn(len(newBase60Chars))]) + for i := 0; i < 20; i++ { + sb.WriteByte(newBase60Chars[randomInt(len(newBase60Chars))]) } return sb.String() } func generateFriendly() string { - rng := rand.New(rand.NewSource(time.Now().UnixNano())) - sets := make([]string, 3) for i := 0; i < 3; i++ { - sets[i] = genFriendlySet(rng) + sets[i] = genFriendlySet() } result := strings.Join(sets, "-") - letterPos := rng.Intn(len(result)) + letterPos := randomInt(len(result)) + // Generate a different position until a non-separator is found for result[letterPos] == '-' { - letterPos = rng.Intn(len(result)) + letterPos = randomInt(len(result)) } existingLetter := result[letterPos] var newLetter string if strings.Contains(consonants, string(existingLetter)) { - newLetter = string(consonants[rng.Intn(len(consonants))]) + newLetter = string(consonants[randomInt(len(consonants))]) } else if strings.Contains(vowels, string(existingLetter)) { - newLetter = string(vowels[rng.Intn(len(vowels))]) + newLetter = string(vowels[randomInt(len(vowels))]) } result = result[:letterPos] + strings.ToUpper(newLetter) + result[letterPos+1:] // Generate a different position until a non-separator and non-uppercase-letter // is found - numberPos := rng.Intn(len(result)) + numberPos := randomInt(len(result)) for result[numberPos] == '-' || result[numberPos] == result[letterPos] { - numberPos = rng.Intn(len(result)) + numberPos = randomInt(len(result)) } - result = result[:numberPos] + string(numbers[rng.Intn(len(numbers))]) + result[numberPos+1:] + result = result[:numberPos] + string(numbers[randomInt(len(numbers))]) + result[numberPos+1:] return result } -func genFriendlySet(rng *rand.Rand) string { +func genFriendlySet() string { set := make([]byte, 6) - set[0] = consonants[rng.Intn(len(consonants))] - set[1] = vowels[rng.Intn(len(vowels))] - set[2] = consonants[rng.Intn(len(consonants))] - set[3] = consonants[rng.Intn(len(consonants))] - set[4] = vowels[rng.Intn(len(vowels))] - set[5] = consonants[rng.Intn(len(consonants))] + set[0] = consonants[randomInt(len(consonants))] + set[1] = vowels[randomInt(len(vowels))] + set[2] = consonants[randomInt(len(consonants))] + set[3] = consonants[randomInt(len(consonants))] + set[4] = vowels[randomInt(len(vowels))] + set[5] = consonants[randomInt(len(consonants))] return string(set) } -func printHelp() { - fmt.Println(`Usage: eow [-h|-t] - --h prints this help message - --t prints a single 20-character NewBase60 (nb60) or friendly (friendly) - password. - -Arguments are positional, so length AND count may be omitted OR count may be -omitted. If specifying count, length must also be specified. When ommitted, -length defaults to 20 and count defaults to 1.`) +func randomInt(max int) int { + n, err := rand.Int(rand.Reader, big.NewInt(int64(max))) + if err != nil { + fmt.Println("Error: could not generate a random number:", err) + os.Exit(1) + } + return int(n.Int64()) }