package main import ( "bufio" "fmt" "golang.org/x/term" "math/rand" "os" "strconv" "strings" "time" ) // Basic constant definitions const ( normNum = 10 // Number of normal passwords specNum = 6 // Number of special passwords passNum = 6 // Number of passphrases maxLength = 255 // Maximum length of passwords rangeMax = 42 // Maximum range for password length rangeMin = 8 // Minimum range for password length passWords = 8 // Number of words in passphrases lower = "abcdefghijklmnopqrstuvwxyz" upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" digit = "0123456789" other = "!$%&#/()=?+-_,.;:<>[]{}|@*" // Special characters for special passwords defaultFile = "/usr/share/dict/words" // Default path to the file of words used for passphrases ) // Variables for different types of passwords var ( alpha []string = strings.Split(lower+upper, "") // Array of alphabets (lowercase and uppercase) alnum []string = append(alpha, strings.Split(digit, "")...) // Array of alphanumeric characters every []string = append(alnum, strings.Split(other, "")...) // Array of all characters (alphabet, digit and special) normal []string = append(alnum, "-", "_") // Characters for normal passwords special []string = every // Characters for special passwords wordlist string = defaultFile // Path to a dictionary to use for passphrases length int = 0 // Length of passwords ) func main() { // // Error handling // var err error // Checking command line argument and environment variable for password length if len(os.Args) == 2 { length, err = strconv.Atoi(os.Args[1]) } else if len(os.Getenv("MAKEPASS_DEFAULT_LENGTH")) > 0 { length, err = strconv.Atoi(os.Getenv("MAKEPASS_DEFAULT_LENGTH")) } // If there is an error in conversion, or if the length is not within limits, exit the program with an error message if err != nil || (length > maxLength || length < 0) { fmt.Printf("Error: Argument must be a whole number between 0 and %d.\n", maxLength) os.Exit(1) } // Get wordlist from env if len(os.Getenv("MAKEPASS_WORDLIST")) > 0 { wordlist = os.Getenv("MAKEPASS_WORDLIST") } // Check if the wordlist file exists if _, err := os.Stat(wordlist); os.IsNotExist(err) { fmt.Println("The file", wordlist, "does not exist") os.Exit(1) } // // move on // // initialise the random seed // TODO: Get seed from /dev/random, like we do in the perl- and zsh-versions? rand.Seed(time.Now().UnixNano()) // get screen width termWidth, _, err := term.GetSize(0) // Read wordlist from file file, err := os.Open(wordlist) if err != nil { fmt.Printf("failed to open file: %s", err) } scanner := bufio.NewScanner(file) scanner.Split(bufio.ScanLines) var text []string for scanner.Scan() { text = append(text, scanner.Text()) } file.Close() // // print passwords // // Generate and print normal and special passwords printColumns("Normal passwords:", 10, normal, termWidth) fmt.Println("") printColumns("Passwords with special characters:", 6, special, termWidth) fmt.Println("") // Generate and print passphrases for i := 0; i < passNum; i++ { fmt.Println(passphrase(&text)) } } // Function to generate a random string of given length from given characters func randstring(chars []string) string { l := length if length == 0 { // Random length if not specified l = rand.Intn(rangeMax-rangeMin) + rangeMin } var str strings.Builder // Add random characters to str for i := 1; i <= l; i++ { if i == 1 || i == l { // don't want passwords to start or end with special characters str.WriteString(alnum[rand.Intn(len(alnum))]) } else { str.WriteString(chars[rand.Intn(len(chars))]) } } return str.String() } // Function to generate and print passwords func printColumns(title string, num int, chars []string, termWidth int) { // column width colWidth := rangeMax + 2 if length > 0 { colWidth = length + 2 } // number of colums columns := termWidth / colWidth if columns == 0 { columns = 1 } // Print title fmt.Println(title) // Print passwords in columns for i := 1; i <= num; i++ { fmt.Printf("%-[1]*[2]s", colWidth, randstring(chars)) if i%columns == 0 || (i == num && i%columns > 0) { // Add newlines fmt.Println("") } } } // Function to generate passphrases func passphrase(arrh *[]string) string { text := *arrh var str strings.Builder for i := 0; i < passWords; i++ { str.WriteString(text[rand.Intn(len(text))]) // Write a random word to the passphrase if i != passWords-1 { str.WriteString("-") // Add hyphen between words } } return str.String() }