Skip to content

Commit

Permalink
passphrase generator
Browse files Browse the repository at this point in the history
  • Loading branch information
jedib0t committed Apr 19, 2024
1 parent 283134b commit 0bfc5eb
Show file tree
Hide file tree
Showing 14 changed files with 79,796 additions and 51 deletions.
7 changes: 2 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
.PHONY: test

default: build
default: test

bench:
go test -bench=. -benchmem ./password

build:
go build ./cmd/password-generator
go test -bench=. -benchmem ./passphrase ./password

cyclo:
gocyclo -over 13 ./*/*.go
Expand Down
81 changes: 59 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,42 @@
[![Coverage Status](https://coveralls.io/repos/github/jedib0t/go-passwords/badge.svg?branch=main)](https://coveralls.io/github/jedib0t/go-passwords?branch=main)
[![Go Report Card](https://goreportcard.com/badge/github.com/jedib0t/go-passwords)](https://goreportcard.com/report/github.com/jedib0t/go-passwords)

Password generation library for GoLang.
Passphrase & Password generation library for GoLang.

## Benchmarks
```
$ go test -bench=. -benchmem ./password
goos: linux
goarch: amd64
pkg: github.com/jedib0t/go-passwords/password
cpu: AMD Ryzen 9 5900X 12-Core Processor
BenchmarkGenerator_Generate-12 6245260 188.2 ns/op 40 B/op 2 allocs/op
BenchmarkSequencer_GotoN-12 4359440 270.6 ns/op 32 B/op 3 allocs/op
BenchmarkSequencer_Next-12 13632730 83.67 ns/op 16 B/op 1 allocs/op
BenchmarkSequencer_NextN-12 6608569 181.5 ns/op 32 B/op 3 allocs/op
BenchmarkSequencer_Prev-12 13509426 87.51 ns/op 16 B/op 1 allocs/op
BenchmarkSequencer_PrevN-12 4266948 276.8 ns/op 32 B/op 3 allocs/op
PASS
ok github.com/jedib0t/go-passwords/password 8.178s
## Passphrases
```golang
generator, err := passphrase.NewGenerator(
passphrase.WithCapitalizedWords(true),
passphrase.WithDictionary(dictionaries.English()),
passphrase.WithNumWords(3),
passphrase.WithNumber(true),
passphrase.WithSeparator("-"),
passphrase.WithWordLength(4, 6),
)
if err != nil {
panic(err.Error())
}
for idx := 1; idx <= 10; idx++ {
fmt.Printf("Passphrase #%3d: %#v\n", idx, generator.Generate())
}
```
<details>
<summary>Output...</summary>
<pre>
Passphrase # 1: "Renest-Apod4-Yowing"
Passphrase # 2: "Lapse-Diplex3-Wekas"
Passphrase # 3: "Banzai-Duster8-Relock"
Passphrase # 4: "Nulled-Mica5-Toads"
Passphrase # 5: "Aughts5-Morro-Welter"
Passphrase # 6: "Moth-Sigh-Pirate5"
Passphrase # 7: "Nonart-Lambs2-Pilot"
Passphrase # 8: "Umbles-Epilog3-Defuse"
Passphrase # 9: "Lignin-Rayons-Rumens5"
Passphrase # 10: "Chrism7-Flunks-Guise"
</pre>
</details>

## Usage

### Random Passwords
## Passwords
```golang
generator, err := password.NewGenerator(
password.WithCharset(password.AllChars.WithoutAmbiguity().WithoutDuplicates()),
Expand Down Expand Up @@ -58,9 +72,8 @@ Password # 10: "toKue=dvUPzz"
</pre>
</details>

## Sequential Passwords
### Sequential Passwords

### In a Loop
```golang
sequencer, err := password.NewSequencer(
password.WithCharset(password.AllChars.WithoutAmbiguity()),
Expand Down Expand Up @@ -94,7 +107,7 @@ Password # 10: "AAAAAAAK"
</pre>
</details>

### Streamed (for async processing)
#### Streamed (for async processing)
```golang
sequencer, err := password.NewSequencer(
password.WithCharset(password.Charset("AB")),
Expand Down Expand Up @@ -166,3 +179,27 @@ Password # 31: "BBBBA"
Password # 32: "BBBBB"
</pre>
</details>

## Benchmarks
```
goos: linux
goarch: amd64
pkg: github.com/jedib0t/go-passwords/passphrase
cpu: AMD Ryzen 9 5900X 12-Core Processor
BenchmarkGenerator_Generate-12 5252654 221.8 ns/op 135 B/op 4 allocs/op
PASS
ok github.com/jedib0t/go-passwords/passphrase 1.410s
goos: linux
goarch: amd64
pkg: github.com/jedib0t/go-passwords/password
cpu: AMD Ryzen 9 5900X 12-Core Processor
BenchmarkGenerator_Generate-12 6397098 186.0 ns/op 40 B/op 2 allocs/op
BenchmarkSequencer_GotoN-12 4321675 273.4 ns/op 32 B/op 3 allocs/op
BenchmarkSequencer_Next-12 14045982 83.89 ns/op 16 B/op 1 allocs/op
BenchmarkSequencer_NextN-12 6548796 183.2 ns/op 32 B/op 3 allocs/op
BenchmarkSequencer_Prev-12 13450102 87.86 ns/op 16 B/op 1 allocs/op
BenchmarkSequencer_PrevN-12 4230694 277.4 ns/op 32 B/op 3 allocs/op
PASS
ok github.com/jedib0t/go-passwords/password 8.239s
```
85 changes: 85 additions & 0 deletions cmd/passphrase-generator/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package main

import (
"flag"
"fmt"
"os"
"strings"
"time"

"github.com/jedib0t/go-passwords/passphrase"
"github.com/jedib0t/go-passwords/passphrase/dictionaries"
)

var (
flagCapitalized = flag.Bool("capitalize", false, "Capitalize all words?")
flagDictionary = flag.String("dictionary", "", "Path to dictionary file (default: built-in English words)")
flagHelp = flag.Bool("help", false, "Display this Help text")
flagNumPassphrases = flag.Int("num-passphrases", 10, "Number of passphrases to generate")
flagNumWords = flag.Int("num-words", 3, "Number of words in Passphrase")
flagPrintIndex = flag.Bool("index", false, "Print Index Value (1-indexed)")
flagSeed = flag.Uint64("seed", 0, "Seed value for non-sequenced mode (ignored if zero)")
flagWithNumber = flag.Bool("with-number", false, "Inject random number suffix?")
)

func main() {
flag.Parse()
if *flagHelp {
printHelp()
os.Exit(0)
}

dictionary := dictionaries.English()
if *flagDictionary != "" {
bytes, err := os.ReadFile(*flagDictionary)
if err != nil {
panic(err.Error())
}
dictionary = strings.Split(string(bytes), "\n")
}
numWords := *flagNumWords
if numWords < 2 {
fmt.Printf("ERROR: value of --num-words way too low: %#v\n", numWords)
os.Exit(1)
} else if numWords > 16 {
fmt.Printf("ERROR: value of --num-words way too high: %#v\n", numWords)
os.Exit(1)
}
printIndex := *flagPrintIndex
seed := *flagSeed
if seed == 0 {
seed = uint64(time.Now().UnixNano())
}

generator, err := passphrase.NewGenerator(
passphrase.WithCapitalizedWords(*flagCapitalized),
passphrase.WithDictionary(dictionary),
passphrase.WithNumWords(numWords),
passphrase.WithNumber(*flagWithNumber),
)
if err != nil {
fmt.Printf("ERROR: failed to instantiate generator: %v\n", err)
os.Exit(1)
}
generator.SetSeed(seed)

for idx := 0; idx < *flagNumPassphrases; idx++ {
if printIndex {
fmt.Printf("%d\t", idx+1)
}
fmt.Println(generator.Generate())
}
}

func printHelp() {
fmt.Println(`passphrase-generator: Generate random passphrases.
Examples:
* passphrase-generator
// generate 10 passphrases
* passphrase-generator --capitalize --num-words=4 --with-number
// generate 10 passphrases with 4 words capitalized and with a number in there
Flags:`)
flag.PrintDefaults()
}
33 changes: 33 additions & 0 deletions passphrase/dictionaries/english.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package dictionaries

import (
_ "embed" // for embedding dictionary files
"sort"
"strings"
"sync"
)

//go:embed english.txt
var englishTxtRaw string

var (
englishWords []string
englishOnce sync.Once
)

func init() {
englishOnce.Do(func() {
englishTxtRaw = strings.ReplaceAll(englishTxtRaw, "\r", "")
englishWords = strings.Split(englishTxtRaw, "\n")
sort.Strings(englishWords)
})
}

// English returns all known English words.
func English() []string {
rsp := make([]string, len(englishWords))
for idx := range rsp {
rsp[idx] = englishWords[idx]
}
return rsp
}
Loading

0 comments on commit 0bfc5eb

Please sign in to comment.