2024-03-12 20:59:07 +01:00
|
|
|
// SPDX-FileCopyrightText: 2021-2024 Winni Neessen <wn@neessen.dev>
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
2023-04-18 11:49:44 +02:00
|
|
|
// Package main is the APG command line client that makes use of the apg-go library
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2023-08-04 16:02:58 +02:00
|
|
|
"flag"
|
2023-08-04 17:14:24 +02:00
|
|
|
"fmt"
|
2023-04-18 11:49:44 +02:00
|
|
|
"os"
|
2024-03-12 20:12:32 +01:00
|
|
|
"runtime"
|
2023-04-18 11:49:44 +02:00
|
|
|
|
2024-03-14 09:37:52 +01:00
|
|
|
"github.com/wneessen/apg-go"
|
2023-04-18 11:49:44 +02:00
|
|
|
)
|
|
|
|
|
2023-08-06 18:55:47 +02:00
|
|
|
// MinimumAmountTooHigh is an error message displayed when a minimum amount of
|
|
|
|
// parameter has been set to a too high value
|
|
|
|
const MinimumAmountTooHigh = "WARNING: You have selected a minimum amount of characters that is bigger\n" +
|
|
|
|
"than 50% of the minimum password length to be generated. This can lead\n" +
|
|
|
|
"to extraordinary calculation times resulting in apg-go never finishing\n" +
|
|
|
|
"the job. Please consider lowering the value.\n\n"
|
|
|
|
|
2023-04-18 11:49:44 +02:00
|
|
|
func main() {
|
2024-03-08 16:02:32 +01:00
|
|
|
config := apg.NewConfig()
|
2023-08-04 16:02:58 +02:00
|
|
|
|
|
|
|
// Configure and parse the CLI flags
|
2023-08-05 14:47:30 +02:00
|
|
|
// See usage() for flag details
|
2024-03-08 16:02:32 +01:00
|
|
|
var algorithm int
|
|
|
|
var modeString string
|
2024-03-12 20:12:32 +01:00
|
|
|
var complexPass, humanReadable, lowerCase, numeric, special, showVer, upperCase bool
|
2024-03-08 16:02:32 +01:00
|
|
|
flag.IntVar(&algorithm, "a", 1, "")
|
2024-03-17 18:09:27 +01:00
|
|
|
flag.BoolVar(&config.BinaryHexMode, "bh", false, "")
|
|
|
|
flag.BoolVar(&config.BinaryNewline, "bn", false, "")
|
2024-03-12 20:41:24 +01:00
|
|
|
flag.BoolVar(&complexPass, "C", false, "")
|
|
|
|
flag.StringVar(&config.ExcludeChars, "E", "", "")
|
|
|
|
flag.Int64Var(&config.FixedLength, "f", 0, "")
|
2024-03-25 11:30:46 +01:00
|
|
|
flag.BoolVar(&config.MobileGrouping, "g", false, "")
|
2024-03-12 20:41:24 +01:00
|
|
|
flag.BoolVar(&humanReadable, "H", false, "")
|
|
|
|
flag.BoolVar(&config.SpellPassword, "l", false, "")
|
2024-03-08 16:02:32 +01:00
|
|
|
flag.BoolVar(&lowerCase, "L", false, "")
|
2024-03-12 20:41:24 +01:00
|
|
|
flag.Int64Var(&config.MinLength, "m", config.MinLength, "")
|
2024-03-08 16:02:32 +01:00
|
|
|
flag.Int64Var(&config.MinLowerCase, "mL", config.MinLowerCase, "")
|
|
|
|
flag.Int64Var(&config.MinNumeric, "mN", config.MinNumeric, "")
|
|
|
|
flag.Int64Var(&config.MinSpecial, "mS", config.MinSpecial, "")
|
2024-03-12 20:41:24 +01:00
|
|
|
flag.Int64Var(&config.MinUpperCase, "mU", config.MinUpperCase, "")
|
2024-03-08 16:02:32 +01:00
|
|
|
flag.Int64Var(&config.NumberPass, "n", config.NumberPass, "")
|
2024-03-12 20:41:24 +01:00
|
|
|
flag.StringVar(&modeString, "M", "", "")
|
|
|
|
flag.BoolVar(&numeric, "N", false, "")
|
2024-03-12 19:00:21 +01:00
|
|
|
flag.BoolVar(&config.CheckHIBP, "p", false, "")
|
2024-03-12 20:41:24 +01:00
|
|
|
flag.BoolVar(&special, "S", false, "")
|
|
|
|
flag.BoolVar(&config.SpellPronounceable, "t", false, "")
|
|
|
|
flag.BoolVar(&upperCase, "U", false, "")
|
2024-03-12 20:12:32 +01:00
|
|
|
flag.BoolVar(&showVer, "v", false, "")
|
2024-03-12 20:41:24 +01:00
|
|
|
flag.Int64Var(&config.MaxLength, "x", config.MaxLength, "")
|
2023-08-04 16:02:58 +02:00
|
|
|
flag.Usage = usage
|
|
|
|
flag.Parse()
|
|
|
|
|
2024-03-12 20:12:32 +01:00
|
|
|
// Show version and exit
|
|
|
|
if showVer {
|
|
|
|
_, _ = os.Stderr.WriteString(`apg-go // A "Automated Password Generator"-clone ` +
|
|
|
|
`v` + apg.VERSION + "\n")
|
|
|
|
_, _ = os.Stderr.WriteString("OS: " + runtime.GOOS + " // Arch: " +
|
|
|
|
runtime.GOARCH + " \n")
|
|
|
|
_, _ = os.Stderr.WriteString("(C) 2021-2024 by Winni Neessen\n")
|
|
|
|
os.Exit(0)
|
|
|
|
}
|
|
|
|
|
2023-08-05 14:47:30 +02:00
|
|
|
// Old style character modes
|
2024-03-14 22:45:20 +01:00
|
|
|
configOldStyle(config, humanReadable, lowerCase, upperCase, numeric,
|
|
|
|
special, complexPass)
|
2023-08-05 14:47:30 +02:00
|
|
|
|
|
|
|
// New style character modes (has higher priority than the old style modes)
|
2024-03-08 16:02:32 +01:00
|
|
|
if modeString != "" {
|
|
|
|
config.Mode = apg.ModesFromFlags(modeString)
|
2023-08-04 17:14:24 +02:00
|
|
|
}
|
2023-08-05 15:04:34 +02:00
|
|
|
|
2023-08-06 18:55:47 +02:00
|
|
|
// For the "minimum amount of" modes we need to imply at the type
|
|
|
|
// of character mode is set
|
2024-03-14 22:45:20 +01:00
|
|
|
configMinRequirement(config)
|
|
|
|
|
|
|
|
// Check if algorithm is supported
|
|
|
|
config.Algorithm = apg.IntToAlgo(algorithm)
|
|
|
|
if config.Algorithm == apg.AlgoUnsupported {
|
|
|
|
_, _ = fmt.Fprintf(os.Stderr, "unsupported algorithm value: %d\n", algorithm)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate the password based on the given flags and print it to stdout
|
|
|
|
generate(config)
|
|
|
|
}
|
|
|
|
|
|
|
|
// configMinRequirement configures the "minimum amount" feature
|
|
|
|
func configMinRequirement(config *apg.Config) {
|
2024-03-08 16:02:32 +01:00
|
|
|
if config.MinLowerCase > 0 {
|
|
|
|
if float64(config.MinLength)/2 < float64(config.MinNumeric) {
|
2023-08-06 18:55:47 +02:00
|
|
|
_, _ = os.Stderr.WriteString(MinimumAmountTooHigh)
|
|
|
|
}
|
2024-03-08 16:02:32 +01:00
|
|
|
config.Mode = apg.MaskSetMode(config.Mode, apg.ModeLowerCase)
|
2023-08-06 18:55:47 +02:00
|
|
|
}
|
2024-03-08 16:02:32 +01:00
|
|
|
if config.MinNumeric > 0 {
|
|
|
|
if float64(config.MinLength)/2 < float64(config.MinLowerCase) {
|
2023-08-06 18:55:47 +02:00
|
|
|
_, _ = os.Stderr.WriteString(MinimumAmountTooHigh)
|
|
|
|
}
|
2024-03-08 16:02:32 +01:00
|
|
|
config.Mode = apg.MaskSetMode(config.Mode, apg.ModeNumeric)
|
2023-08-06 18:55:47 +02:00
|
|
|
}
|
2024-03-08 16:02:32 +01:00
|
|
|
if config.MinSpecial > 0 {
|
|
|
|
if float64(config.MinLength)/2 < float64(config.MinSpecial) {
|
2023-08-06 18:55:47 +02:00
|
|
|
_, _ = os.Stderr.WriteString(MinimumAmountTooHigh)
|
|
|
|
}
|
2024-03-08 16:02:32 +01:00
|
|
|
config.Mode = apg.MaskSetMode(config.Mode, apg.ModeSpecial)
|
2023-08-06 18:55:47 +02:00
|
|
|
}
|
2024-03-08 16:02:32 +01:00
|
|
|
if config.MinUpperCase > 0 {
|
|
|
|
if float64(config.MinLength)/2 < float64(config.MinUpperCase) {
|
2023-08-06 18:55:47 +02:00
|
|
|
_, _ = os.Stderr.WriteString(MinimumAmountTooHigh)
|
|
|
|
}
|
2024-03-08 16:02:32 +01:00
|
|
|
config.Mode = apg.MaskSetMode(config.Mode, apg.ModeUpperCase)
|
2023-08-06 18:55:47 +02:00
|
|
|
}
|
2024-03-14 22:45:20 +01:00
|
|
|
}
|
2023-08-06 18:55:47 +02:00
|
|
|
|
2024-03-14 22:45:20 +01:00
|
|
|
// configOldStyle configures the old style character modes
|
|
|
|
func configOldStyle(config *apg.Config, humanReadable, lowerCase, upperCase,
|
2024-03-14 22:57:07 +01:00
|
|
|
numeric, special, complexPass bool,
|
|
|
|
) {
|
2024-03-14 22:45:20 +01:00
|
|
|
if humanReadable {
|
|
|
|
config.Mode = apg.MaskToggleMode(config.Mode, apg.ModeHumanReadable)
|
|
|
|
}
|
|
|
|
if lowerCase {
|
|
|
|
config.Mode = apg.MaskToggleMode(config.Mode, apg.ModeLowerCase)
|
|
|
|
}
|
|
|
|
if upperCase {
|
|
|
|
config.Mode = apg.MaskToggleMode(config.Mode, apg.ModeUpperCase)
|
|
|
|
}
|
|
|
|
if numeric {
|
|
|
|
config.Mode = apg.MaskToggleMode(config.Mode, apg.ModeNumeric)
|
|
|
|
}
|
|
|
|
if special {
|
|
|
|
config.Mode = apg.MaskToggleMode(config.Mode, apg.ModeSpecial)
|
|
|
|
}
|
|
|
|
if complexPass {
|
|
|
|
config.Mode = apg.MaskSetMode(config.Mode, apg.ModeLowerCase|apg.ModeNumeric|
|
|
|
|
apg.ModeSpecial|apg.ModeUpperCase)
|
|
|
|
config.Mode = apg.MaskClearMode(config.Mode, apg.ModeHumanReadable)
|
2023-08-05 18:10:11 +02:00
|
|
|
}
|
2024-03-14 22:45:20 +01:00
|
|
|
}
|
2023-08-05 18:10:11 +02:00
|
|
|
|
2024-03-14 22:45:20 +01:00
|
|
|
func generate(config *apg.Config) {
|
2024-03-08 16:02:32 +01:00
|
|
|
generator := apg.New(config)
|
2024-03-17 18:09:27 +01:00
|
|
|
|
|
|
|
// In binary mode we only generate a single secret
|
|
|
|
if config.Algorithm == apg.AlgoBinary {
|
|
|
|
password, err := generator.Generate()
|
|
|
|
if err != nil {
|
|
|
|
_, _ = fmt.Fprintf(os.Stderr, "failed to generate password: %s\n", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
if config.BinaryNewline {
|
|
|
|
fmt.Println(password)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
fmt.Print(password)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// For any other mode we cycle through the amount of passwords to be generated
|
2024-03-08 16:02:32 +01:00
|
|
|
for i := int64(0); i < config.NumberPass; i++ {
|
|
|
|
password, err := generator.Generate()
|
2023-08-04 16:02:58 +02:00
|
|
|
if err != nil {
|
2023-08-05 18:10:11 +02:00
|
|
|
_, _ = fmt.Fprintf(os.Stderr, "failed to generate password: %s\n", err)
|
|
|
|
os.Exit(1)
|
2023-08-04 16:02:58 +02:00
|
|
|
}
|
2024-03-12 18:28:01 +01:00
|
|
|
if config.Algorithm == apg.AlgoRandom && config.SpellPassword {
|
2024-03-08 16:02:32 +01:00
|
|
|
spellPass, err := apg.Spell(password)
|
|
|
|
if err != nil {
|
|
|
|
_, _ = fmt.Fprintf(os.Stderr, "failed to spell password: %s\n", err)
|
|
|
|
}
|
|
|
|
fmt.Printf("%s (%s)\n", password, spellPass)
|
|
|
|
continue
|
|
|
|
}
|
2024-03-12 18:28:01 +01:00
|
|
|
if config.Algorithm == apg.AlgoPronounceable && config.SpellPronounceable {
|
|
|
|
pronouncePass, err := generator.Pronounce()
|
|
|
|
if err != nil {
|
|
|
|
_, _ = fmt.Fprintf(os.Stderr, "failed to pronounce password: %s\n", err)
|
|
|
|
}
|
|
|
|
fmt.Printf("%s (%s)\n", password, pronouncePass)
|
|
|
|
continue
|
|
|
|
}
|
2024-03-08 16:02:32 +01:00
|
|
|
fmt.Println(password)
|
2024-03-12 19:00:21 +01:00
|
|
|
|
|
|
|
if config.CheckHIBP {
|
|
|
|
pwned, err := apg.HasBeenPwned(password)
|
|
|
|
if err != nil {
|
|
|
|
_, _ = fmt.Fprintf(os.Stderr, "failed to check HIBP database: %s\n", err)
|
|
|
|
}
|
|
|
|
if pwned {
|
|
|
|
fmt.Print("^-- !!WARNING: The previously generated password was found in " +
|
|
|
|
"HIBP database. Do not use it!!\n")
|
|
|
|
}
|
|
|
|
}
|
2023-08-05 15:04:34 +02:00
|
|
|
}
|
2023-08-04 16:02:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// usage is used by the flag package to display the CLI usage message
|
|
|
|
func usage() {
|
|
|
|
// Usage text
|
2023-08-05 15:14:43 +02:00
|
|
|
const ut = `apg-go v` +
|
|
|
|
apg.VERSION + "\n" +
|
2024-03-14 09:37:52 +01:00
|
|
|
`A OSS "Automated Password Generator"-clone -- https://github.com/wneessen/apg-go/
|
2024-03-07 21:15:49 +01:00
|
|
|
Created 2021-2024 by Winni Neessen (MIT licensed)
|
2023-08-04 16:02:58 +02:00
|
|
|
|
|
|
|
apg [-a <algo>] [-m <length>] [-x <length>] [-L] [-U] [-N] [-S] [-H] [-C]
|
2024-03-12 20:43:48 +01:00
|
|
|
[-l] [-M mode] [-E char_string] [-n num_of_pass] [-mX number] [-t] [-p] [-v] [-h]
|
2023-08-04 16:02:58 +02:00
|
|
|
|
2023-08-05 15:14:43 +02:00
|
|
|
Flags:
|
2023-08-04 16:02:58 +02:00
|
|
|
-a ALGORITH Choose the password generation algorithm (Default: 1)
|
2023-08-05 14:47:30 +02:00
|
|
|
- 0: pronounceable password generation (koremutake syllables)
|
|
|
|
- 1: random password generation according to password modes/flags
|
2023-08-05 18:10:11 +02:00
|
|
|
- 2: coinflip (returns heads or tails)
|
2024-03-17 18:09:27 +01:00
|
|
|
- 3: full binary mode (generates simple 256 bit randomness)
|
|
|
|
-bh When set, will print the generated secret in its hex representation (Default: off)
|
|
|
|
-bn When set, will return a new line character after the generated secret (Default: off)
|
|
|
|
- Note: The -bX options only apply to binary mode (Algo: 3)
|
2023-08-04 16:02:58 +02:00
|
|
|
-m LENGTH Minimum length of the password to be generated (Default: 12)
|
|
|
|
-x LENGTH Maximum length of the password to be generated (Default: 20)
|
2023-08-05 15:04:34 +02:00
|
|
|
-f LENGTH Fixed length of the password to be generated (Ignores -m and -x)
|
2024-03-12 18:28:01 +01:00
|
|
|
- Note: Due to the way the pronounceable password algorithm works,
|
|
|
|
this setting might not always apply
|
2024-03-25 11:30:46 +01:00
|
|
|
-g When set, mobile-friendly character grouping will be enabled in Algo: 1
|
|
|
|
- Note: Grouping characters in random passwords makes them much
|
|
|
|
more predictable and lowers the entropy of the generated password.
|
2023-08-04 16:02:58 +02:00
|
|
|
-n NUMBER Amount of password to be generated (Default: 6)
|
2024-03-17 18:09:27 +01:00
|
|
|
- Note: Does not apply to binary mode (Algo: 3)
|
2023-08-04 16:02:58 +02:00
|
|
|
-E CHARS List of characters to be excluded in the generated password
|
2023-08-05 15:14:43 +02:00
|
|
|
-M [LUNSHClunshc] New style password flags
|
2023-08-05 14:47:30 +02:00
|
|
|
- Note: new-style flags have higher priority than any of the old-style flags
|
2023-08-06 18:55:47 +02:00
|
|
|
-mL NUMBER Minimum amount of lower-case characters (implies -L)
|
2024-03-07 21:51:52 +01:00
|
|
|
-mN NUMBER Minimum amount of numeric characters (implies -N)
|
|
|
|
-mS NUMBER Minimum amount of special characters (implies -S)
|
|
|
|
-mU NUMBER Minimum amount of upper-case characters (implies -U)
|
2023-08-06 18:55:47 +02:00
|
|
|
- Note: any of the "Minimum amount of" modes may result in
|
|
|
|
extraordinarily long calculation times
|
2024-03-12 18:28:01 +01:00
|
|
|
- Note 2: The "minimum amount of" modes do not apply in
|
|
|
|
pronounceable mode (-a 0)
|
2023-08-05 17:54:41 +02:00
|
|
|
-C Enable complex password mode (implies -L -U -N -S and disables -H)
|
|
|
|
-H Avoid ambiguous characters in passwords (i. e.: 1, l, I, O, 0) (Default: off)
|
|
|
|
-L Toggle lower-case characters in passwords (Default: on)
|
2023-08-05 14:47:30 +02:00
|
|
|
-N Toggle numeric characters in passwords (Default: on)
|
|
|
|
-S Toggle special characters in passwords (Default: off)
|
2023-08-05 17:54:41 +02:00
|
|
|
-U Toggle upper-case characters in passwords (Default: on)
|
2023-08-05 14:47:30 +02:00
|
|
|
- Note: this flag has higher priority than the other old-style flags
|
2023-08-04 16:02:58 +02:00
|
|
|
-l Spell generated passwords in phonetic alphabet (Default: off)
|
2024-03-12 18:28:01 +01:00
|
|
|
-t Spell generated pronounceable passwords with the corresponding
|
|
|
|
syllables (Default: off)
|
2023-08-04 16:02:58 +02:00
|
|
|
-p Check the HIBP database if the generated passwords was found in a leak before (Default: off)
|
2023-08-05 14:47:30 +02:00
|
|
|
- Note: this feature requires internet connectivity
|
2023-08-04 16:02:58 +02:00
|
|
|
-h Show this help text
|
2023-08-05 15:14:43 +02:00
|
|
|
-v Show version string`
|
2023-08-04 16:02:58 +02:00
|
|
|
|
2023-08-05 15:14:43 +02:00
|
|
|
_, _ = os.Stderr.WriteString(ut + "\n\n")
|
2023-04-18 11:49:44 +02:00
|
|
|
}
|