diff --git a/.gitignore b/.gitignore index 66fd13c..285d217 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ *.dll *.so *.dylib +apg # Test binary, built with `go test -c` *.test diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..73f69e0 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/apg.go.iml b/.idea/apg.go.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/apg.go.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..09dbac8 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..6385bbc --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 900337c..655265a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,30 @@ # apg.go -Advanced Password Generator Clone +_apg.pl_ is a simple APG-like password generator script written in Go. It tries to replicate the functionality of the "[Automated Password Generator](https://web.archive.org/web/20130313042424/http://www.adel.nursat.kz:80/apg)", which hasn't been maintained since 2003. Since more and more Unix distributions are abondoning the tool, I was looking for an alternative. FreeBSD for example recommends "security/makepasswd", which is also written in Perl but requires much more dependency packages and doesn't offer the feature-set/flexibility of APG. Therefore I decided to write my own implementation. As I never used the "pronouncable password" functionality, I left this out in my version. + +## Usage +Simply add the execute-flag to the script and run it +```sh +$ chmod +x apg +$ ./apg +``` + +## Systemwide installation +To be a proper APG replacement, i suggest to install it into a directory in your PATH and symlink it to "apg": +```sh +$ sudo cp apg /usr/local/bin/apg +``` + +## CLI options +_apg.go_ replicates some of the parameters of the original APG. Some parameters are different though: + +- ```-m, --minpasslen ```: The minimum length of the password to be generated +- ```-x, --maxpasslen ```: The maximum length of the password to be generated +- ```-n, --numofpass ```: The amount of passwords to be generated +- ```-E, --exclude ```: Do not use the specified characters in generated passwords +- ```-U, --uppercase```: Use uppercase characters in passwords +- ```-N, --numbers```: Use numeric characters in passwords +- ```-S, --special```: Use special characters in passwords +- ```-H, --human```: Avoid ambiguous characters in passwords (i. e.: 1, l, I, o, O, 0) +- ```-c, --complex```: Generate complex passwords (implies -U -N -S and disables -H) +- ```-h, --help```: Show a CLI help text +- ```-v, --version```: Show the version number \ No newline at end of file diff --git a/apg.go b/apg.go new file mode 100644 index 0000000..fdc7965 --- /dev/null +++ b/apg.go @@ -0,0 +1,136 @@ +package main + +import ( + "crypto/rand" + "flag" + "fmt" + "log" + "math/big" + "os" +) + +// Constants +const DefaultPwLenght int = 20 +const VersionString string = "0.1.0" +const PwLowerCharsHuman string = "abcdefghjkmnpqrstuvwxyz" +const PwUpperCharsHuman string = "ABCDEFGHJKMNPQRSTUVWXYZ" +const PwLowerChars string = "abcdefghijklmnopqrstuvwxyz" +const PwUpperChars string = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +const PwSpecialCharsHuman string = "\"#/\\$%&+-*" +const PwSpecialChars string = "\"#/!\\$%&+-*.,?=()[]{}:;~^|" +const PwNumbersHuman string = "23456789" +const PwNumbers string = "1234567890" + +type cliOpts struct { + minPassLen int + maxPassLen int + numOfPass int + useComplex bool + useLowerCase bool + useUpperCase bool + useNumber bool + useSpecial bool + humanReadable bool + excludeChars string + showHelp bool + showVersion bool +} + +var config cliOpts + +// Read flags +func init() { + // Bool flags + flag.BoolVar(&config.useLowerCase, "L", true, "Use lower case characters in passwords") + flag.BoolVar(&config.useUpperCase, "U", false, "Use upper case characters in passwords") + flag.BoolVar(&config.useNumber, "N", false, "Use numbers in passwords") + flag.BoolVar(&config.useSpecial, "S", false, "Use special characters in passwords") + flag.BoolVar(&config.useComplex, "C", true, "Generate complex passwords (implies -L -U -N -S, disables -H)") + flag.BoolVar(&config.humanReadable, "H", false, "Generate human-readable passwords") + flag.BoolVar(&config.showVersion, "v", false, "Show version") + + // Int flags + flag.IntVar(&config.minPassLen, "m", 10, "Minimum password length") + flag.IntVar(&config.maxPassLen, "x", DefaultPwLenght, "Maxiumum password length") + flag.IntVar(&config.numOfPass, "n", 1, "Number of passwords to generate") + + // TODO: Exclude chars are missing, yet + + flag.Parse() + if config.showVersion { + _, _ = os.Stderr.WriteString("Advanced Password Generator v" + VersionString + "\n") + os.Exit(0) + } +} + +func main() { + pwLength := config.minPassLen + if pwLength < config.minPassLen { + pwLength = config.minPassLen + } + if pwLength > config.maxPassLen { + pwLength = config.maxPassLen + } + if config.useComplex { + config.useUpperCase = true + config.useLowerCase = true + config.useSpecial = true + config.useNumber = true + config.humanReadable = false + } + + pwUpperChars := PwUpperChars + pwLowerChars := PwLowerChars + pwNumbers := PwNumbers + pwSpecialChars := PwSpecialChars + if config.humanReadable { + pwUpperChars = PwUpperCharsHuman + pwLowerChars = PwLowerCharsHuman + pwNumbers = PwNumbersHuman + pwSpecialChars = PwSpecialCharsHuman + } + + var charRange string + if config.useLowerCase { + charRange = charRange + pwLowerChars + } + if config.useUpperCase { + charRange = charRange + pwUpperChars + } + if config.useNumber { + charRange = charRange + pwNumbers + } + if config.useSpecial { + charRange = charRange + pwSpecialChars + } + + /* + for i := 1; i <= config.numOfPass; i++ { + fmt.Println(i) + } + */ + pwString := getRandChar(&charRange, pwLength) + fmt.Println(pwString) + +} + +func getRandChar(charRange *string, pwLength int) string { + availCharsLength := len(*charRange) + charSlice := []byte(*charRange) + returnString := []byte{} + for i := 0; i < pwLength; i++ { + randNum := getRandNum(availCharsLength) + returnString = append(returnString, charSlice[randNum]) + } + return string(returnString) +} + +func getRandNum(maxNum int) int { + maxNumBigInt := big.NewInt(int64(maxNum)) + randNum64, err := rand.Int(rand.Reader, maxNumBigInt) + if err != nil { + log.Fatal("An error occured generating random number: %v", err) + } + randNum := int(randNum64.Int64()) + return randNum +}