Skip to content

Commit

Permalink
2022
Browse files Browse the repository at this point in the history
  • Loading branch information
vuon9 committed Feb 4, 2022
1 parent 5070e42 commit 4ce7530
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 163 deletions.
63 changes: 36 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,45 +1,54 @@
# pwgen-go
Random password CLI generator practice in Go. It is inspired by https://github.com/jbernard/pwgen

![Go](https://github.com/vuon9/pwgen-go/workflows/Go/badge.svg)

## Download

```bash
go get -u github.com/vuon9/pwgen-go
go install github.com/vuon9/pwgen-go
```

## Debug

```PWGEN_GO_DEBUG=true```

## Usages

```md
> pwgen-go -help
Usage: pwgen-go [ OPTIONS ] [pw_length] [num_pw]
Options supported by pwgen-go:
-h or -help
-ambiguous, -B (default: false)
Don't include ambiguous characters in the password
-column, (default: false)
Print the generated passwords in columns
-debug, -vvv (default: false)
Enable debug mode
-help, -h (default: false)
Get help
-c or -capitalize
Include at least one capital letter in the password
-A or -no-capitalize
-no-capitalize, -A (default: true)
Don't include capital letters in the password
-n or -numerals
Include at least one number in the password
-0 or -no-numerals
-no-numerals, -0 (default: true)
Don't include numbers in the password
-y or -symbol
Include at least one special symbol in the password
-r <chars> or --remove-chars=<chars>
Remove characters from the set of characters to generate passwords
-H or -sha1=path/to/file[#seed]
Use sha1 hash of given file as a (not so) random generator
-B or -ambiguous
Don't include ambiguous characters in the password
-v or -no-vowels
Do not use any vowels so as to avoid accidental nasty words
-s or -secure
-no-vowels, -v (default: false)
Don't include any vowels so as to avoid accidental nasty words
-remove-chars, -r (default: <empty>)
Remove characters from the set of characters to generate passwords (ex: -r <chars> or --remove-chars=<chars>)
-secure, -s (default: false)
Generate completely random passwords
-column
Print the generated passwords in columns
-no-column
Don't print the generated passwords in columns
-vvv or -debug
Enable debug mode
-sha1, -H (default: <empty>)
Use sha1 hash of given file as a (not so) random generator (ex: -H or -sha1=path/to/file[#seed])
-symbol, -y (default: false)
Include at least one special symbol in the password
```

## Examples

```
> pwgen-go -column -remove-chars="bzaa" 20 22
ldtovroffwuvklekjnvm nenkcpheqdoipdthvnjm fucgnkuytggileohwsve xgwdoqvwouqmkslsiqvh
olirhswrjkquohpxsfit ofokhpnksdyedktxvxtn prlkkmsecvvdkwwfgtlk rcnfokrewyerjfcjndrs
qniywvgejecychuetrqh cxsquoueyvxjnlownnwx fsyopntknulmniqeinwx leemsrwtfmrmmmvxtpjg
mispnpnoemlhitgrtjhh odkunevrtcvrsrysipfp umixnivwqhilqwuskxhw dcohkgoukhrysdyfwmho
fyenfrysthmfqirfvonx mctxoocsgskxevgmhiev uokntlwwrmthjlwrghqk sgfhfxfkskdljoxtmwfv
hxigrlmjrqikflupkvcx ntfjmdqcilchrthwwmyp
```
163 changes: 84 additions & 79 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const (
PW_UPPERS = 2
PW_SYMBOLS = 4
PW_AMBIGUOUS = 8
PW_NO_VOWELS = 10
PW_VOWELS = 10

pwDigits = "0123456789"
pwUppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Expand Down Expand Up @@ -80,47 +80,23 @@ func getOptions(pwArgs []int, pwOptions *pwOptions) (*pwOptions, error) {
}

var (
cmdCapitalize cmdName = "capitalize"
cmdNoCapitalize cmdName = "no-capitalize"
cmdHelp cmdName = "help"
cmdNumerals cmdName = "numerals"
cmdNoNumerals cmdName = "no-numerals"
cmdSymbol cmdName = "symbol"
cmdNoVowels cmdName = "no-vowels"
cmdRemoveChars cmdName = "remove-chars"
cmdHelp cmdName = "help"
cmdSymbol cmdName = "symbol"
cmdSha1 cmdName = "sha1"
cmdAmbiguous cmdName = "ambiguous"
cmdNoVowels cmdName = "no-vowels"
cmdSecure cmdName = "secure"
cmdColumn cmdName = "column"
cmdNoColumn cmdName = "no-column"
cmdDebug cmdName = "debug"
)

func main() {
commands := NewCommandController(
NewItems(
NewBoolCommand(cmdHelp, "h", "false", "Get help"),
NewBoolCommand(cmdCapitalize, "c", "false", "Include at least one capital letter in the password"),
NewBoolCommand(cmdNoCapitalize, "A", "true", "Don't include capital letters in the password"),
NewBoolCommand(cmdNumerals, "n", "false", "Include at least one number in the password"),
NewBoolCommand(cmdNoNumerals, "0", "true", "Don't include numbers in the password"),
NewBoolCommand(cmdSymbol, "y", "false", "Include at least one special symbol in the password"),
NewStringCommand(cmdRemoveChars, "r", "", "Remove characters from the set of characters to generate passwords (ex: -r <chars> or --remove-chars=<chars>)"),
NewStringCommand(cmdSha1, "H", "", "Use sha1 hash of given file as a (not so) random generator (ex: -H or -sha1=path/to/file[#seed])"),
NewBoolCommand(cmdAmbiguous, "B", "false", "Don't include ambiguous characters in the password"),
NewBoolCommand(cmdNoVowels, "v", "false", "Do not use any vowels so as to avoid accidental nasty words"),
NewBoolCommand(cmdSecure, "s", "false", "Generate completely random passwords"),
NewBoolCommand(cmdColumn, "", "false", "Print the generated passwords in columns"),
NewBoolCommand(cmdNoColumn, "", "true", "Don't print the generated passwords in columns"),
NewBoolCommand(cmdDebug, "vvv", "false", "Enable debug mode"),
),
WithUsageHeader("Usage: pwgen-go [ OPTIONS ] [pw_length] [num_pw]\nOptions supported by pwgen-go:"),
)

commands.Ready()
commands := newCommands()

var hasSha1 string = commands.GetString("sha1")
if hasSha1 != "" {
if hasSha1 := commands.GetString(cmdSha1); hasSha1 != "" {
splitted := strings.Split(hasSha1, "#")
if len(splitted) != 2 {
println("err: Sha1 filepath and seed are invalid, should be path/sub_path/file.extension#seed")
Expand All @@ -139,56 +115,65 @@ func main() {
}

var pwFlags byte
pwFlags |= PW_DIGITS | PW_UPPERS
var withColumn bool
removeChars := ""
var debug bool

switch {
case commands.GetBool(cmdCapitalize):
pwFlags |= PW_UPPERS
fallthrough
case commands.GetBool(cmdNoCapitalize):
pwFlags &^= PW_UPPERS
case commands.GetBool(cmdNumerals):
pwFlags |= PW_DIGITS
fallthrough
case commands.GetBool(cmdNoNumerals):
pwFlags ^= PW_DIGITS
fallthrough
case commands.GetBool(cmdSecure):
pwFlags = PW_DIGITS | PW_UPPERS
case commands.GetBool(cmdSymbol):
pwFlags |= PW_SYMBOLS
case commands.GetBool(cmdAmbiguous):
pwFlags |= PW_AMBIGUOUS
case commands.GetBool(cmdNoVowels):
pwFlags |= PW_NO_VOWELS | PW_DIGITS | PW_UPPERS
case commands.GetBool(cmdNoColumn):
withColumn = false
case commands.GetBool(cmdColumn):
withColumn = true
case commands.GetString(cmdRemoveChars) != "":
removeChars = commands.GetString(cmdRemoveChars)
case commands.GetBool(cmdDebug):
debug = true
case commands.GetBool(cmdHelp):
commands.Usage()
os.Exit(0)

commands.isDebug, _ = strconv.ParseBool(os.Getenv("PWGEN_GO_DEBUG"))

optionFuncs := []struct {
optCheck func() bool
optFlagFn func()
}{
{
optCheck: func() bool { return commands.GetBool(cmdNoCapitalize) },
optFlagFn: func() { pwFlags &^= PW_UPPERS },
},
{
optCheck: func() bool { return commands.GetBool(cmdNoNumerals) },
optFlagFn: func() { pwFlags &^= PW_DIGITS },
},
{
optCheck: func() bool { return commands.GetBool(cmdNoVowels) },
optFlagFn: func() { pwFlags &^= PW_VOWELS },
},
{
optCheck: func() bool { return commands.GetBool(cmdSecure) },
optFlagFn: func() { pwFlags |= PW_DIGITS | PW_UPPERS },
},
{
optCheck: func() bool { return commands.GetBool(cmdSymbol) },
optFlagFn: func() { pwFlags |= PW_SYMBOLS },
},
{
optCheck: func() bool { return commands.GetBool(cmdAmbiguous) },
optFlagFn: func() { pwFlags |= PW_AMBIGUOUS },
},
{
optCheck: func() bool { return commands.GetBool(cmdColumn) },
optFlagFn: func() { withColumn = true },
},
{
optCheck: func() bool { return commands.GetBool(cmdHelp) },
optFlagFn: func() {
commands.Usage()
os.Exit(0)
},
},
}

// Randomize passwords by flags & eligible chars
var t1 time.Time
if debug {
t1 = time.Now()
for _, opt := range optionFuncs {
if opt.optCheck() {
opt.optFlagFn()
}
}

removeChars := commands.GetString(cmdRemoveChars)
passwords, err := pwRand(nil, pwOptions, eligibleChars(pwFlags, removeChars))
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}

// Print passwords by column or no column
const itemsPerColumn = 4
for i, pwd := range passwords {
fmt.Printf("%s\t", pwd)
Expand All @@ -197,24 +182,46 @@ func main() {
}
}

if debug {
fmt.Println("\nElapsed time: ", time.Since(t1))
}
os.Exit(0)
}

func newCommands() *baseCommand {
commands := NewCommandController(
NewItems(
NewBoolCommand(cmdHelp, "h", false, "Get help"),
NewBoolCommand(cmdNoCapitalize, "A", true, "Don't include capital letters in the password"),
NewBoolCommand(cmdNoNumerals, "0", true, "Don't include numbers in the password"),
NewBoolCommand(cmdAmbiguous, "B", false, "Don't include ambiguous characters in the password"),
NewBoolCommand(cmdNoVowels, "v", false, "Don't include any vowels so as to avoid accidental nasty words"),
NewBoolCommand(cmdSymbol, "y", false, "Include at least one special symbol in the password"),
NewStringCommand(cmdRemoveChars, "r", "", "Remove characters from the set of characters to generate passwords (ex: -r <chars> or --remove-chars=<chars>)"),
NewStringCommand(cmdSha1, "H", "", "Use sha1 hash of given file as a (not so) random generator (ex: -H or -sha1=path/to/file[#seed])"),
NewBoolCommand(cmdSecure, "s", false, "Generate completely random passwords"),
NewBoolCommand(cmdColumn, "", false, "Print the generated passwords in columns"),
NewBoolCommand(cmdDebug, "vvv", false, "Enable debug mode"),
),
WithUsageHeader("Usage: pwgen-go [ OPTIONS ] [pw_length] [num_pw]\nOptions supported by pwgen-go:"),
)

commands.Ready()

return commands
}

func sha1File(filePath string, seed string) {
f, err := os.Open(filePath)
if err != nil {
println("err: Couldn't open file")
os.Exit(0)
}
defer f.Close()

defer func() {
_ = f.Close()
}()

h := hmac.New(sha1.New, []byte(seed))
if _, err := io.Copy(h, f); err != nil {
println("err: Couldn't has file content")
os.Exit(0)
}

var s string
Expand All @@ -241,7 +248,7 @@ func eligibleChars(pwFlags byte, removeChars string) string {
chars += pwAmbiguous
}

if (pwFlags & PW_NO_VOWELS) == 0 {
if (pwFlags & PW_VOWELS) != 0 {
chars += pwVowels
}

Expand All @@ -267,11 +274,9 @@ func pwRand(buf *string, pwOptions *pwOptions, chars string) ([]string, error) {
}

rand.Seed(time.Now().UnixNano())

var wg sync.WaitGroup
wg.Add(pwOptions.numPw)

passwords := make([]string, pwOptions.numPw)
wg := sync.WaitGroup{}
wg.Add(pwOptions.numPw)
for i := range passwords {
go func(i int) {
defer wg.Done()
Expand Down
Loading

0 comments on commit 4ce7530

Please sign in to comment.