diff --git a/main.go b/main.go index 6ce7547..74f718d 100644 --- a/main.go +++ b/main.go @@ -1,74 +1,94 @@ // GoPass // Author: Maximilian Patterson -// Version: 1.2.2 package main import ( - cryptorand "crypto/rand" - "encoding/binary" + "crypto/rand" "fmt" "math/big" - mathrand "math/rand" "os" "strconv" + "strings" ) var allowedCharacters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890`~!@#$%^&*()_+[]\\{}|;':,./<>?") -func main() { - // Take in all OS arg - args := os.Args[1:] - if len(args) < 1 { - println("No password length specified! (ex: ./gopass 16)") - return +const ( + Version = "1.3.0" + symbols = "`~!@#$%^&*()_+[]\\{}|;':,./<>?" +) + +func matchArguments(args []string) string { + // If there are no arguments + if len(args) == 0 { + return "No password length specified! (ex: gopass 16)" } - // Convert String arg to int - size, err := strconv.Atoi(args[0]) - if err != nil { - println("First argument supplied must be an integer! (ex: 16)") - return + // First argument is special, must be an integer, -v, or -h + var size = 0 // Password length + err := error(nil) + if size, err = strconv.Atoi(args[0]); err == nil { // If first argument is an integer + } else if args[0] == "-v" { + return "GoPass version " + Version + } else if args[0] == "-h" { + return "GoPass - A simple password generator written in Go\n" + + "Usage: gopass [length] [disallowed characters] [optional remove symbols -s]\n" + + " Example: gopass 16\n" + + " Example: gopass 16 -r=abc123!@#\n" + + " Example: gopass 16 -s\n" + + "\nFor help (this output): gopass -h\n" + + "For version: gopass -v\n" } - // Grab second argument (if it exists) and use it as a disallowed character(s) - var disallowed []rune - if len(args) == 2 { - // Break apart the string into a slice of runes - disallowed = []rune(args[1]) - - // Remove all disallowed characters from the allowedCharacters slice - for _, r := range disallowed { - for i, v := range allowedCharacters { - if v == r { - allowedCharacters = append(allowedCharacters[:i], allowedCharacters[i+1:]...) - } - } + for i := 1; i < len(args); i++ { + v := args[i] + if v == "-s" { + removeDisallowed([]rune(symbols)) + } else if strings.HasPrefix(v, "-r=") { // If argument starts with -r= + // Remove all characters after the = until next whitespace + removeDisallowed([]rune(v[2:])) + } else { + return "Invalid argument (\"" + v + "\") supplied! (Type gopass -h for help)" } } + if size <= 0 { + return "No/invalid password length specified! (ex: gopass 16)" + } else { + return generatePassword(size) + } +} + +// Remove all disallowed characters from the allowedCharacters slice +func removeDisallowed(disallowed []rune) { + for _, r := range disallowed { + for i, v := range allowedCharacters { + if v == r { + allowedCharacters = append(allowedCharacters[:i], allowedCharacters[i+1:]...) + } + } + } +} + +func generatePassword(size int) string { // Make empty array of runes with size of size pass := make([]rune, size) - // Seed rand with time - var b [8]byte - _, err = cryptorand.Read(b[:]) - if err != nil { - println("Error securely seeding crypto/rand!") - return - } - mathrand.Seed(int64(binary.LittleEndian.Uint64(b[:]))) - // Assign every slot of pass to a random allowedCharacter for i := range pass { // Generate a random int greater than 0 and not to exceed the length of allowedCharacters - index, err := cryptorand.Int(cryptorand.Reader, big.NewInt(int64(len(allowedCharacters)))) + index, err := rand.Int(rand.Reader, big.NewInt(int64(len(allowedCharacters)))) if err != nil { println("Error securely generating random character!") - return + return "" } pass[i] = allowedCharacters[index.Int64()] } - // Print the password - fmt.Println(string(pass)) + return string(pass) +} + +func main() { + // Process arguments + fmt.Println(matchArguments(os.Args[1:])) }