aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--README.md58
-rw-r--r--go/go.mod7
-rw-r--r--go/go.sum4
-rw-r--r--go/makepass.go171
5 files changed, 227 insertions, 16 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e720fd5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+# ignore binaries
+
+/makepass.go
diff --git a/README.md b/README.md
index 93bd739..6258c88 100644
--- a/README.md
+++ b/README.md
@@ -82,26 +82,52 @@ nice job.
Here are the results so far:
```
-% hyperfine --time-unit=millisecond --warmup=1 --shell=none ./*(.)
+% hyperfine --time-unit=millisecond --warmup=1 --shell=none ./makepass.*
Benchmark 1: ./makepass.bash
- Time (mean ± σ): 469.3 ms ± 3.6 ms [User: 92.0 ms, System: 734.0 ms]
- Range (min … max): 463.6 ms … 474.3 ms 10 runs
+ Time (mean ± σ): 422.7 ms ± 3.1 ms [User: 95.0 ms, System: 659.0 ms]
+ Range (min … max): 417.9 ms … 428.3 ms 10 runs
-Benchmark 2: ./makepass.pl
- Time (mean ± σ): 38.8 ms ± 0.4 ms [User: 18.2 ms, System: 19.0 ms]
- Range (min … max): 38.1 ms … 40.1 ms 77 runs
+Benchmark 2: ./makepass.go
+ Time (mean ± σ): 6.9 ms ± 0.2 ms [User: 1.1 ms, System: 4.7 ms]
+ Range (min … max): 6.3 ms … 8.1 ms 434 runs
-Benchmark 3: ./makepass.sh
- Time (mean ± σ): 1261.6 ms ± 18.5 ms [User: 298.0 ms, System: 2030.0 ms]
- Range (min … max): 1237.4 ms … 1291.8 ms 10 runs
+Benchmark 3: ./makepass.pl
+ Time (mean ± σ): 36.2 ms ± 0.4 ms [User: 17.8 ms, System: 16.2 ms]
+ Range (min … max): 35.6 ms … 37.9 ms 82 runs
-Benchmark 4: ./makepass.zsh
- Time (mean ± σ): 27.2 ms ± 0.7 ms [User: 12.0 ms, System: 13.0 ms]
- Range (min … max): 25.5 ms … 29.1 ms 111 runs
+Benchmark 4: ./makepass.sh
+ Time (mean ± σ): 1107.7 ms ± 15.3 ms [User: 276.0 ms, System: 1790.0 ms]
+ Range (min … max): 1084.9 ms … 1140.3 ms 10 runs
+
+Benchmark 5: ./makepass.zsh
+ Time (mean ± σ): 25.3 ms ± 0.7 ms [User: 12.7 ms, System: 10.8 ms]
+ Range (min … max): 23.6 ms … 27.7 ms 114 runs
Summary
- './makepass.zsh' ran
- 1.43 ± 0.04 times faster than './makepass.pl'
- 17.27 ± 0.49 times faster than './makepass.bash'
- 46.43 ± 1.43 times faster than './makepass.sh'
+ './makepass.go' ran
+ 3.69 ± 0.15 times faster than './makepass.zsh'
+ 5.28 ± 0.17 times faster than './makepass.pl'
+ 61.62 ± 1.86 times faster than './makepass.bash'
+ 161.48 ± 5.22 times faster than './makepass.sh'
+```
+
+## Versions
+
+### Bash
+This version needs a bit more work.
+
+### Go
+Build width (from root folder in repo):
```
+$ go build -o ../makepass.go -C go/ -ldflags "-s -w" makepass.go
+```
+
+### Perl
+Perl version. I like this version.
+
+### Shell
+Needs more work.
+
+### Zsh
+This is currently the "main" version. It uses pure zsh, with no forking out to
+other programs. As of adding the go-version, it is the second fastest version.
diff --git a/go/go.mod b/go/go.mod
new file mode 100644
index 0000000..bc5ed12
--- /dev/null
+++ b/go/go.mod
@@ -0,0 +1,7 @@
+module makepass
+
+go 1.20
+
+require golang.org/x/term v0.12.0
+
+require golang.org/x/sys v0.12.0 // indirect
diff --git a/go/go.sum b/go/go.sum
new file mode 100644
index 0000000..8c01403
--- /dev/null
+++ b/go/go.sum
@@ -0,0 +1,4 @@
+golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
+golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU=
+golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
diff --git a/go/makepass.go b/go/makepass.go
new file mode 100644
index 0000000..542b63e
--- /dev/null
+++ b/go/makepass.go
@@ -0,0 +1,171 @@
+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()
+}