Remove old code for the v2 refactor

This commit is contained in:
Winni Neessen 2023-04-06 12:21:20 +02:00
parent 565a714cc6
commit befa6c2723
Signed by: wneessen
GPG key ID: 385AC9889632126E
23 changed files with 0 additions and 1530 deletions

View file

@ -1,23 +0,0 @@
container:
image: golang:latest
env:
GOPROXY: https://proxy.golang.org
lint_task:
name: GolangCI Lint
container:
image: golangci/golangci-lint:latest
run_script: golangci-lint run -v --timeout 5m0s --out-format json > lint-report.json
always:
golangci_artifacts:
path: lint-report.json
type: text/json
format: golangci
build_task:
modules_cache:
folder: $GOPATH/pkg/mod
get_script: go get github.com/wneessen/apg-go/cmd/apg
build_script: go build github.com/wneessen/apg-go/cmd/apg
test_script: go test github.com/wneessen/apg-go/cmd/apg

19
.gitignore vendored
View file

@ -1,19 +0,0 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
apg
bin
.go
build
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/

View file

@ -1,128 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
github+coc@neessen.net.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

View file

@ -1,20 +0,0 @@
## Build first
FROM golang:latest as builder
RUN mkdir /builddir
ADD . /builddir/
WORKDIR /builddir
RUN CGO_ENABLED=0 go build -a -installsuffix cgo -ldflags '-w -s -extldflags "-static"' -o apg-go \
github.com/wneessen/apg-go/cmd/apg
## Create scratch image
FROM scratch
LABEL maintainer="wn@neessen.net"
COPY ["docker-files/passwd", "/etc/passwd"]
COPY ["docker-files/group", "/etc/group"]
COPY --from=builder ["/etc/ssl/certs/ca-certificates.crt", "/etc/ssl/cert.pem"]
COPY --chown=apg-go ["LICENSE", "/apg-go/LICENSE"]
COPY --chown=apg-go ["README.md", "/apg-go/README.md"]
COPY --from=builder --chown=apg-go ["/builddir/apg-go", "/apg-go/apg-go"]
WORKDIR /apg-go
USER apg-go
ENTRYPOINT ["/apg-go/apg-go"]

21
LICENSE
View file

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2021 Winni Neessen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

272
README.md
View file

@ -1,272 +0,0 @@
# A "Automated Password Generator"-clone
[![Go Reference](https://pkg.go.dev/badge/github.com/wneessen/apg-go.svg)](https://pkg.go.dev/github.com/wneessen/apg-go) [![Go Report Card](https://goreportcard.com/badge/github.com/wneessen/apg-go)](https://goreportcard.com/report/github.com/wneessen/apg-go) [![Build Status](https://api.cirrus-ci.com/github/wneessen/apg-go.svg)](https://cirrus-ci.com/github/wneessen/apg-go) ![CodeQL workflow](https://github.com/wneessen/apg-go/actions/workflows/codeql-analysis.yml/badge.svg) <a href="https://ko-fi.com/D1D24V9IX"><img src="https://uploads-ssl.webflow.com/5c14e387dab576fe667689cf/5cbed8a4ae2b88347c06c923_BuyMeACoffee_blue.png" height="20" alt="buy ma a coffee"></a>
_apg-go_ is a simple APG-like password generator 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 written in Perl
but requires a lot of dependency packages and doesn't offer the feature-set/flexibility of APG.
Since FIPS-181 (pronouncable passwords) has been withdrawn in 2015, apg-go does not follow this standard. Instead
it implements the [Koremutake Syllables System](https://shorl.com/koremutake.php) in its pronouncable password mode.
## Installation
### Docker
There is a ready-to-use Docker image hosted on Github.
* Download the image:
```shell
$ docker pull ghcr.io/wneessen/apg-go:main
```
* Run the image:
```shell
$ docker run ghcr.io/wneessen/apg-go:main
```
### Ports/Packages
#### FreeBSD
apg-go can be found as `/security/apg` in the [FreeBSD ports](https://cgit.freebsd.org/ports/tree/security/apg)
tree.
#### Arch Linux
Find apg-go in [Arch Linux AUR](https://aur.archlinux.org/packages/apg-go/). \
Alternatively use the [PKGBUILD](https://github.com/wneessen/apg-go/tree/main/buildfiles/arch-linux) file
in this git repository
### Binary releases
#### Linux/BSD/MacOS
* Download release
```sh
$ curl -LO https://github.com/wneessen/apg-go/releases/download/v<version>/apg-v<version>-<os>-<architecture>.tar.gz
$ curl -LO https://github.com/wneessen/apg-go/releases/download/v<version>/apg-v<version>-<os>-<architecture>.tar.gz.sha256
```
* Verify the checksum
```sh
$ sha256 apg-v<version>-<os>-<architecture>.tar.gz
$ cat apg-v<version>-<os>-<architecture>.tar.gz.sha256
```
**Make sure the checksum of the downloaded file and the checksum in the .sha256 match**
* Extract archive
```sh
$ tar xzf apg-v<version>-<os>-<architecture>.tar.gz
```
* Execute
```sh
$ ./apg
```
#### Windows
* Download release
```PowerShell
PS> Invoke-RestMethod -Uri https://github.com/wneessen/apg-go/releases/download/v<version>/apg-v<version>-windows-<architecture>.zip -OutFile apg-v<version>-windows-<architecure>.zip
PS> Invoke-RestMethod -Uri https://github.com/wneessen/apg-go/releases/download/v<version>/apg-v<version>-windows-<architecture>.zip.sha256 -OutFile apg-v<version>-windows-<architecure>.zip.sha256
```
* Verify the checksum
```PowerShell
PS> Get-FileHash apg-v<version>-windows-<architecture>.zip | Format-List
PS> type apg-v<version>-windows-<architecture>.zip.sha256
```
**Make sure the checksum of the downloaded file and the checksum in the .sha256 match**
* Extract archive
```PowerShell
PS> Expand-Archive -LiteralPath apg-v<version>-windows-<architecture>
```
* Execute
```PowerShell
PS> cd apg-v<version>-windows-<architecture>
PS> apg.exe
```
### Sources
* Download sources
```sh
$ curl -LO https://github.com/wneessen/apg-go/archive/refs/tags/v<version>.tar.gz
```
* Extract source
```sh
$ tar xzf v<version>.tar.gz
```
* Build binary
```sh
$ cd apg-go-<version>
$ go build -o apg ./...
```
* Execute the brand new binary
```sh
$ ./apg
```
### Systemwide installation
It is recommed to install apg in a directory of your ```$PATH``` environment. To do so run:
(In this example we use ```/usr/local/bin``` as system-wide binary path. YMMV)
```sh
$ sudo cp apg /usr/local/bin/apg
```
## Programmatic interface
Since v0.4.0 the CLI and the main package functionality have been separated from each other, which makes
it easier to use the `apg-go` package in other Go code as well. This way you can make of the password
generation in your own code without having to rely on the actual apg-go binary.
Code examples on how to use the package can be found in the [example-code](example-code) directory.
## Usage examples
### Default behaviour
By default apg-go will generate 6 passwords, with a minimum length of 12 characters and a
maxiumum length of 20 characters. The generated password will use a character set constructed
from lower case, upper case and numeric characters.
```shell
$ ./apg-go
R8rCC8bw5NvJmTUK2g
cHB9qogTbfdzFgnH
hoHfpWAHHSNa4Q
QyjscIsZkQGh
904YqsU5SnoqLo2w
utdFKXdeiXFzM
```
### Modifying the character sets
#### Old style
Let's assume you want to generate a single password, constructed out of upper case, numeric
and special characters. Since lower case is part of the default set, you would need to disable them
by setting the `-L` parameter. In addition you would set the `-S` parameter to enable special
characters. Finally the parameter `-n 1` is needed to keep apg-go from generating more than one
password:
```shell
$ ./apg-go -n 1 -L -S
XY7>}H@5U40&_A1*9I$
```
#### New/modern style
Since the old style switches can be kind of confusing, it is recommended to use the "new style"
parameters instead. The new style is all combined in the `-M` parameter. Using the upper case
version of a parameter argument enables a feature, while the lower case version disabled it. The
previous example could be represented like this in new style:
```shell
$ ./apg-go -n 1 -M lUSN
$</K?*|M)%8\U$5JA5~
```
#### Human readability
Generated passwords can sometimes be a bit hard to read for humans, especially when ambiguous
characters are part of the password. Some characters in the ASCII character set look similar to
each other. In example it can be hard to differentiate an upper case I from a lower case l.
Same applies to the number zero (0) and the upper case O. To not run into issues with human
readability, you can set the `-H` parameter to toggle on the "human readable" feature. When the
option is set, apg-go will avoid using any of the typical ambiguous characters in the generated
passwords.
```shell
$ ./apg-go -n 1 -M LUSN -H
YpranThY3b6b5%\6ARx
```
#### Character exclusion
Let's assume, that for whatever reason, your generated password can never include a colon (:) sign. For
this specific case, you can use the `-E` parameter to specify a list of characters that are to be excluded
from the password generation character set:
```shell
$ ./apg-go -n 1 -M lUSN -H -E :
~B2\%E_|\VV|/5C7EF=
```
#### Complex passwords
If you want to generate complex passwords, there is a shortcut for this as well. By setting the `-C`
parameter, apg-go will automatically default to the most secure settings. The complex parameter
basically implies that the password will use all available characters (lower case, upper case,
numeric and special) and will make sure that human readability is disabled.
```shell
$ ./apg-go -n 1 -C
{q6cvz9le5_fo"X7
```
### Password length
By default, apg-go will generate a password with a random length between 12 and 20 characters. If you
want to be more specific, you can use the `-m` and `-x` parameters to override the defaults. Let's
assume you want a single complex password with a length of exactly 32 characters, you can do so by
running:
```shell
$ ./apg-go -n 1 -C -m 32 -x 32
5lc&HBvx=!EUY*;'/t&>B|~sudhtyDBu
```
### Password spelling
If you need to read out a password, it can be helpful to know the corresponding word for that character in
the phonetic alphabet. By setting the `-l` parameter, agp-go will provide you with the phonetic spelling
(english language) of your newly created password:
```shell
$ ./apg-go -n 1 -M LUSN -H -E : -l
fUTDKeFsU+zn3r= (foxtrot/Uniform/Tango/Delta/Kilo/echo/Foxtrot/sierra/Uniform/PLUS_SIGN/zulu/november/THREE/romeo/EQUAL_SIGN)
```
### Pronouncable passwords
Since v0.4.0 apg-go supports pronouncable passwords, anologous to the original c-apg using the `-a 0`
flag. The original c-apg implemented FIPS-181, which was withdrawn in 2015 for generating pronouncable
passwords. Since the standard is not recommended anymore, `apg-go` instead make use of the
[Koremutake Syllables System](https://shorl.com/koremutake.php). Similar to the original apg, `agp-go`
will automatically randomly add special characters and number (from the human-readable pool) to each
generated pronouncable password. Additionally it will perform a "coinflip" for each Koremutake syllable
and decided if it should switch the case of one of the characters to an upper-case character.
Using the `-t` parameter, `apg-go` will display a spelled out version of the pronouncable password, where
each syllable or number/special character is seperated with a "-" (dash) and if the syllable is not a
Koremutake syllable the character will be spelled out the same was as with activated `-l` in the
non-pronouncable password mode (`-a 1`).
**Note on password length**: The `-m` and `-x` parameters will work in prouncable password mode, but
please keep in mind, that due to the nature how syllables work, your generated password might exceed
the desired length by one complete syllable (which can be up to 3 characters long).
**Security consideration:** Please keep in mind, that pronouncable passwords are less secure then truly
randomly created passwords, due to the nature how syllables work. As a rule of thumb, it is recommended
to multiply the length of your generated pronouncable passwords by at least 1.5 times, compared to truly
randomly generated passwords. It might also be helpful to run the pronoucable password mode with enabled
"[HIBP](#have-i-been-pwned)" flag, so that each generated password is automatically checked against "Have I Been Pwned"
database.
```shell
$ ./apg-go -a 0 -n 1
KebrutinernMy
$ ./apg-go -a 0 -n 1 -m 15 -x 15 -t
pEnbocydrageT*En (pEn-bo-cy-dra-geT-ASTERISK-En)
```
### Have I Been Pwned
Even though, the passwords that apg-go generated for you, are secure, there is a minimal chance, that
someone on the planet used exactly the same password before and that this person was part of an
internet leak or hack, which exposed the password to the public. Such passwords are not considered
secure anymore as they usually land on public available password lists, that are used by crackers.
To be on the safe side, you can use the `-p` parameter, to enable a HIBP check. When the feature is
enabled, apg-go will check the HIBP database at https://haveibeenpwned.com if that password has been
leaked before and provide you with a warning if that is the case.
Please be aware, that this is a live check against the HIBP API, which not only requires internet
connectivity, but also might take between 500ms to 1s to complete. When you generating a bigger list
of password `-n 100`, the process could take much longer than without the `-p` feature enabled.
## CLI parameters
_apg-go_ replicates most of the parameters of the original c-apg. Some parameters are different though:
- `-a <algorithm>`: Choose password generation algorithm (Default: 1)
- `0`: Pronouncable password generation (Koremutake syllables)
- `1`: Random password generation according to password modes/flags
- `-m <length>`: The minimum length of the password to be generated (Default: 12)
- `-x <length>`: The maximum length of the password to be generated (Default: 20)
- `-n <number of passwords>`: The amount of passwords to be generated (Default: 6)
- `-E <list of characters>`: Do not use the specified characters in generated passwords
- `-M <[LUNSHClunshc]>`: New style password parameters (upper-case enables, lower-case disables)
- `-L`: Use lower-case characters in passwords (Default: on)
- `-U`: Use upper-case characters in passwords (Default: on)
- `-N`: Use numeric characters in passwords (Default: on)
- `-S`: Use special characters in passwords (Default: off)
- `-H`: Avoid ambiguous characters in passwords (i. e.: 1, l, I, o, O, 0) (Default: off)
- `-C`: Generate complex passwords (implies -L -U -N -S and disables -H) (Default: off)
- `-l`: Spell generated passwords in random password mode (Default: off)
- `-t`: Spell generated passwords in pronouncable password mode (Default: off)
- `-p`: Check the HIBP database if the generated passwords was found in a leak before (Default: off) // *this feature requires internet connectivity*
- `-h`: Show a CLI help text
- `-v`: Show the version number
## Contributors
Thanks to the following people for contributing to the apg-go codebase:
* [Romain Tartière](https://github.com/smortex)
* [Abraham Ingersoll](https://github.com/aberoham)
* [Vinícius Zavam](https://github.com/egypcio) (Maintaining the FreeBSD port)

View file

@ -1,6 +0,0 @@
# Security Policy
## Reporting a Vulnerability
Please send a mail to apg-go@pebcak.de when you found a security issue in apg-go, even when you are not 100% certain
that it is actually a security issue. Typically, you will receive an answer within a day or even within a few hours.

View file

@ -1,32 +0,0 @@
# Maintainer: "Winni Neessen (https://pebcak.de)
pkgname=apg-go
pkgver=0.4.1
pkgrel=1
pkgdesc='A "Automated Password Generator"-clone'
arch=('i686' 'x86_64' 'armv6h' 'armv7h' 'aarch64')
url='https://github.com/wneessen/apg-go'
license=('MIT')
makedepends=('go')
source=("https://github.com/wneessen/${pkgname}/archive/refs/tags/v${pkgver}.tar.gz")
sha256sums=('64769495843e2c59fc5513a106e58f8751f1649ff8bf6c38cd141322523deea8')
prepare() {
cd "${pkgname}-${pkgver}"
mkdir -p build/
}
build() {
cd "${pkgname}-${pkgver}"
go build -ldflags="-s -w" -o build/${pkgname} github.com/wneessen/apg-go/cmd/apg
}
package() {
# binary
install -D -m755 "${srcdir}/${pkgname}-${pkgver}/build/${pkgname}" \
"${pkgdir}/usr/bin/apg"
# license
install -Dm644 "${srcdir}/${pkgname}-${pkgver}/LICENSE" \
"${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
}

View file

@ -1,21 +0,0 @@
# $OpenBSD$
COMMENT = "automated password generator" clone written in Go
GH_ACCOUNT = wneessen
GH_PROJECT = apg-go
GH_TAGNAME = v0.3.2
CATEGORIES = security
MAINTAINER = Winni Neessen <wn@neessen.net>
# MIT
PERMIT_PACKAGE = Yes
MODULES = lang/go
MODGO_TYPE = bin
ALL_TARGET = wneessen/apg-go/cmd/apg
.include <bsd.port.mk>

View file

@ -1,2 +0,0 @@
SHA256 (apg-go-0.3.2.tar.gz) = QvCC0vVNHLIOHW1jwdkjJVtxEVHJNwQfZBZBgHWM4OQ=
SIZE (apg-go-0.3.2.tar.gz) = 20114

View file

@ -1,14 +0,0 @@
apg-go is a simple APG-like password generator written in Go.
It tries to replicate the functionality of the "Automated Password Generator",
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 written in Perl but requires a lot
of dependency packages and doesn't offer the feature-set/flexibility of APG.
Since FIPS-181 (pronouncable passwords) has been withdrawn in 2015, there is
no use in replicating that feature. Therfore apg-go does not support
pronouncable passwords.
For feature requests or bug reports, please create an issue in the Github
repository at https://github.com/wneessen/apg-go

View file

@ -1,2 +0,0 @@
@comment $OpenBSD: PLIST,v$
@bin bin/apg-go

View file

@ -1,64 +0,0 @@
package chars
import (
"github.com/wneessen/apg-go/config"
"regexp"
)
// PwLowerCharsHuman is the range of lower-case characters in human-readable mode
const PwLowerCharsHuman string = "abcdefghjkmnpqrstuvwxyz"
// PwUpperCharsHuman is the range of upper-case characters in human-readable mode
const PwUpperCharsHuman string = "ABCDEFGHJKMNPQRSTUVWXYZ"
// PwLowerChars is the range of lower-case characters
const PwLowerChars string = "abcdefghijklmnopqrstuvwxyz"
// PwUpperChars is the range of upper-case characters
const PwUpperChars string = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
// PwSpecialCharsHuman is the range of special characters in human-readable mode
const PwSpecialCharsHuman string = "\"#%*+-/:;=\\_|~"
// PwSpecialChars is the range of special characters
const PwSpecialChars string = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
// PwNumbersHuman is the range of numbers in human-readable mode
const PwNumbersHuman string = "23456789"
// PwNumbers is the range of numbers
const PwNumbers string = "1234567890"
// GetRange provides the range of available characters based on configured parameters
func GetRange(config *config.Config) string {
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
}
if config.ExcludeChars != "" {
regExp := regexp.MustCompile("[" + regexp.QuoteMeta(config.ExcludeChars) + "]")
charRange = regExp.ReplaceAllLiteralString(charRange, "")
}
return charRange
}

View file

@ -1,22 +0,0 @@
package chars
// KoremutakeSyllables is a slightly modified Koremutake syllables list based on
// the mechanism described on https://shorl.com/koremutake.php
var KoremutakeSyllables = []string{"ba", "be", "bi", "bo", "bu", "by", "da", "de", "di",
"do", "du", "dy", "fe", "fi", "fo", "fu", "fy", "ga", "ge", "gi", "go", "gu",
"gy", "ha", "he", "hi", "ho", "hu", "hy", "ja", "je", "ji", "jo", "ju", "jy",
"ka", "ke", "ko", "ku", "ky", "la", "le", "li", "lo", "lu", "ly", "ma",
"me", "mi", "mo", "mu", "my", "na", "ne", "ni", "no", "nu", "ny", "pa", "pe",
"pi", "po", "pu", "py", "ra", "re", "ri", "ro", "ru", "ry", "sa", "se",
"si", "so", "su", "sy", "ta", "te", "ti", "to", "tu", "ty", "va", "ve", "vi",
"vo", "vu", "vy", "bra", "bre", "bri", "bro", "bru", "bry", "dra", "dre",
"dri", "dro", "dru", "dry", "fra", "fre", "fri", "fro", "fru", "fry", "gra",
"gre", "gri", "gro", "gru", "gry", "pra", "pre", "pri", "pro", "pru",
"pry", "sta", "ste", "sti", "sto", "stu", "sty", "tra", "tre", "er", "ed",
"in", "ex", "al", "en", "an", "ad", "or", "at", "ca", "ap", "el", "ci", "an",
"et", "it", "ob", "of", "af", "au", "cy", "im", "op", "co", "up", "ing",
"con", "ter", "com", "per", "ble", "der", "cal", "man", "est", "for", "mer",
"col", "ful", "get", "low", "son", "tle", "day", "pen", "pre", "ten",
"tor", "ver", "ber", "can", "ple", "fer", "gen", "den", "mag", "sub", "sur",
"men", "min", "out", "tal", "but", "cit", "cle", "cov", "dif", "ern",
"eve", "hap", "ket", "nal", "sup", "ted", "tem", "tin", "tro", "tro"}

View file

@ -1,148 +0,0 @@
package main
import (
"flag"
"fmt"
"github.com/wneessen/apg-go/chars"
"github.com/wneessen/apg-go/config"
"github.com/wneessen/apg-go/random"
"github.com/wneessen/apg-go/spelling"
"github.com/wneessen/go-hibp"
"log"
"os"
"runtime"
"strings"
"time"
)
// VersionString represents the current version of the apg-go CLI
const VersionString string = "0.4.1"
// Help text
const usage = `apg-go // A "Automated Password Generator"-clone
Copyright (c) 2021 Winni Neessen
apg [-a <algo>] [-m <length>] [-x <length>] [-L] [-U] [-N] [-S] [-H] [-C]
[-l] [-M mode] [-E char_string] [-n num_of_pass] [-v] [-h] [-t]
Options:
-a ALGORITH Choose the password generation algorithm (Default: 1)
- 0: pronounceable password generation (koremutake syllables)
- 1: random password generation according to password modes/flags
-m LENGTH Minimum length of the password to be generated (Default: 12)
-x LENGTH Maximum length of the password to be generated (Default: 20)
-n NUMBER Amount of password to be generated (Default: 6)
-E CHARS List of characters to be excluded in the generated password
-M [LUNSHClunshc] New style password parameters (upper case: on, lower case: off)
-L Use lower case characters in passwords (Default: on)
-U Use upper case characters in passwords (Default: on)
-N Use numeric characters in passwords (Default: on)
-S Use special characters in passwords (Default: off)
-H Avoid ambiguous characters in passwords (i. e.: 1, l, I, O, 0) (Default: off)
-C Enable complex password mode (implies -L -U -N -S and disables -H) (Default: off)
-l Spell generated passwords in phonetic alphabet (Default: off)
-p Check the HIBP database if the generated passwords was found in a leak before (Default: off)
- Note: this feature requires internet connectivity
-h Show this help text
-v Show version string`
// Main function that generated the passwords and returns them
func main() {
// Log configuration
log.SetFlags(log.Ltime | log.Ldate | log.Lshortfile)
// Read and parse flags
flag.Usage = func() { _, _ = fmt.Fprintf(os.Stderr, "%s\n", usage) }
var cfgObj = config.New()
// Show version and exit
if cfgObj.ShowVersion {
_, _ = os.Stderr.WriteString(`apg-go // A "Automated Password Generator"-clone v` + VersionString + "\n")
_, _ = os.Stderr.WriteString("OS: " + runtime.GOOS + " // Arch: " + runtime.GOARCH + " \n")
_, _ = os.Stderr.WriteString("(C) 2021 by Winni Neessen\n")
os.Exit(0)
}
pwList := make([]string, 0)
sylList := map[string][]string{}
// Choose the type of password generation based on the selected algo
for i := 0; i < cfgObj.NumOfPass; i++ {
pwLength := config.GetPwLengthFromParams(&cfgObj)
switch cfgObj.PwAlgo {
case 0:
pwString := ""
pwSyls := make([]string, 0)
charSylSet := chars.KoremutakeSyllables
charSylSet = append(charSylSet,
strings.Split(chars.PwNumbersHuman, "")...)
charSylSet = append(charSylSet,
strings.Split(chars.PwSpecialCharsHuman, "")...)
charSylSetLen := len(charSylSet)
for len(pwString) < pwLength {
randNum, err := random.GetNum(charSylSetLen)
if err != nil {
log.Fatalf("error generating Koremutake syllable: %s", err)
}
nextSyl := charSylSet[randNum]
if random.CoinFlip() {
sylLen := len(nextSyl)
charPos, err := random.GetNum(sylLen)
if err != nil {
log.Fatalf("error generating random number: %s", err)
}
ucChar := string(nextSyl[charPos])
nextSyl = strings.ReplaceAll(nextSyl, ucChar, strings.ToUpper(ucChar))
}
pwString += nextSyl
pwSyls = append(pwSyls, nextSyl)
}
pwList = append(pwList, pwString)
sylList[pwString] = pwSyls
default:
charRange := chars.GetRange(&cfgObj)
pwString, err := random.GetChar(charRange, pwLength)
if err != nil {
log.Fatalf("error generating random character range: %s\n", err)
}
pwList = append(pwList, pwString)
}
}
for _, p := range pwList {
switch cfgObj.OutputMode {
case 1:
spelledPw, err := spelling.String(p)
if err != nil {
log.Fatalf("error spelling out password: %s\n", err)
}
fmt.Printf("%v (%v)\n", p, spelledPw)
case 2:
fmt.Printf("%s", p)
if cfgObj.SpellPron {
spelledPw, err := spelling.Koremutake(sylList[p])
if err != nil {
log.Fatalf("error spelling out password: %s", err)
}
fmt.Printf(" (%s)", spelledPw)
}
fmt.Println()
default:
fmt.Println(p)
}
if cfgObj.CheckHibp {
hc := hibp.New(hibp.WithHTTPTimeout(time.Second*2), hibp.WithPwnedPadding())
pwnObj, _, err := hc.PwnedPassAPI.CheckPassword(p)
if err != nil {
log.Printf("unable to check HIBP database: %v", err)
}
if pwnObj != nil && pwnObj.Count != 0 {
fmt.Print("^-- !!WARNING: The previously generated password was found in HIBP database. Do not use it!!\n")
}
}
}
}

View file

@ -1,288 +0,0 @@
package main
import (
"github.com/wneessen/apg-go/chars"
"github.com/wneessen/apg-go/config"
"github.com/wneessen/apg-go/random"
"github.com/wneessen/apg-go/spelling"
"testing"
)
var cfgObj config.Config
// Make sure the flags are initialized
var _ = func() bool {
testing.Init()
cfgObj = config.New()
return true
}()
// Test getRandNum with max 1000
func TestGetRandNum(t *testing.T) {
testTable := []struct {
testName string
givenVal int
maxRet int
minRet int
shouldFail bool
}{
{"randNum up to 1000", 1000, 1000, 0, false},
{"randNum should be 1", 1, 1, 0, false},
{"randNum should fail on 0", 0, 0, 0, true},
{"randNum should fail on negative", -1, 0, 0, true},
}
for _, testCase := range testTable {
t.Run(testCase.testName, func(t *testing.T) {
randNum, err := random.GetNum(testCase.givenVal)
if testCase.shouldFail {
if err == nil {
t.Errorf("Random number generation succeeded but was expected to fail. Given: %v, returned: %v",
testCase.givenVal, randNum)
}
} else {
if err != nil {
t.Errorf("Random number generation failed: %v", err.Error())
}
if randNum > testCase.maxRet {
t.Errorf("Random number generation returned too big value. Given %v, expected max: %v, got: %v",
testCase.givenVal, testCase.maxRet, randNum)
}
if randNum < testCase.minRet {
t.Errorf("Random number generation returned too small value. Given %v, expected max: %v, got: %v",
testCase.givenVal, testCase.minRet, randNum)
}
}
})
}
}
// Test Pwlength
func TestGenLength(t *testing.T) {
testTable := []struct {
testName string
minLength int
maxLength int
}{
{"pwLength defaults", config.DefaultMinLength, config.DefaultMaxLength},
{"pwLength 0 to 1", 0, 1},
{"pwLength 1 to 10", 0, 10},
{"pwLength 10 to 100", 10, 100},
}
charRange := chars.GetRange(&cfgObj)
for _, testCase := range testTable {
t.Run(testCase.testName, func(t *testing.T) {
cfgObj.MinPassLen = testCase.minLength
cfgObj.MaxPassLen = testCase.maxLength
pwLength := config.GetPwLengthFromParams(&cfgObj)
for i := 0; i < 1000; i++ {
pwString, err := random.GetChar(charRange, pwLength)
if err != nil {
t.Errorf("getRandChar returned an error: %q", err)
}
retLen := len(pwString)
if retLen > testCase.maxLength {
t.Errorf("Generated password length too long. GivenMin %v, GivenMax: %v, Returned length %v",
testCase.minLength, testCase.maxLength, retLen)
}
if retLen < testCase.minLength {
t.Errorf("Generated password length too short. GivenMin %v, GivenMax: %v, Returned length %v",
testCase.minLength, testCase.maxLength, retLen)
}
}
})
}
}
// Test getRandChar
func TestGetRandChar(t *testing.T) {
t.Run("return_value_is_A_B_or_C", func(t *testing.T) {
charRange := "ABC"
randChar, err := random.GetChar(charRange, 1)
if err != nil {
t.Fatalf("Random character generation failed => %v", err.Error())
}
if randChar != "A" && randChar != "B" && randChar != "C" {
t.Fatalf("Random character generation failed. Expected A, B or C but got: %v", randChar)
}
})
t.Run("return_value_has_specific_length", func(t *testing.T) {
charRange := "ABC"
randChar, err := random.GetChar(charRange, 1000)
if err != nil {
t.Fatalf("Random character generation failed => %v", err.Error())
}
if len(randChar) != 1000 {
t.Fatalf("Generated random characters with 1000 chars returned wrong amount of chars: %v",
len(randChar))
}
})
t.Run("fail", func(t *testing.T) {
charRange := "ABC"
randChar, err := random.GetChar(charRange, -2000)
if err == nil {
t.Fatalf("Generated random characters expected to fail, but returned a value => %v",
randChar)
}
})
}
// Test getCharRange() with different cfgObj settings
func TestGetCharRange(t *testing.T) {
lowerCaseBytes := []int{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}
lowerCaseHumanBytes := []int{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r',
's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}
upperCaseBytes := []int{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}
upperCaseHumanBytes := []int{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q',
'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}
numberBytes := []int{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
numberHumanBytes := []int{'2', '3', '4', '5', '6', '7', '8', '9'}
specialBytes := []int{'!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':',
';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~'}
specialHumanBytes := []int{'"', '#', '%', '*', '+', '-', '/', ':', ';', '=', '\\', '_', '|', '~'}
testTable := []struct {
testName string
allowedBytes []int
useLowerCase bool
useUpperCase bool
useNumber bool
useSpecial bool
humanReadable bool
}{
{"lowercase_only", lowerCaseBytes, true, false, false, false, false},
{"lowercase_only_human", lowerCaseHumanBytes, true, false, false, false, true},
{"uppercase_only", upperCaseBytes, false, true, false, false, false},
{"uppercase_only_human", upperCaseHumanBytes, false, true, false, false, true},
{"number_only", numberBytes, false, false, true, false, false},
{"number_only_human", numberHumanBytes, false, false, true, false, true},
{"special_only", specialBytes, false, false, false, true, false},
{"special_only_human", specialHumanBytes, false, false, false, true, true},
}
for _, testCase := range testTable {
t.Run(testCase.testName, func(t *testing.T) {
cfgObj.UseLowerCase = testCase.useLowerCase
cfgObj.UseUpperCase = testCase.useUpperCase
cfgObj.UseNumber = testCase.useNumber
cfgObj.UseSpecial = testCase.useSpecial
cfgObj.HumanReadable = testCase.humanReadable
charRange := chars.GetRange(&cfgObj)
for _, curChar := range charRange {
searchAllowedBytes := containsByte(testCase.allowedBytes, int(curChar), t)
if !searchAllowedBytes {
t.Errorf("Character range returned invalid value: %v", string(curChar))
}
}
})
}
}
// Test Conversions
func TestConvert(t *testing.T) {
testTable := []struct {
testName string
givenVal byte
expVal string
shouldFail bool
}{
{"convert_A_to_Alfa", 'A', "Alfa", false},
{"convert_a_to_alfa", 'a', "alfa", false},
{"convert_0_to_ZERO", '0', "ZERO", false},
{"convert_/_to_SLASH", '/', "SLASH", false},
}
for _, testCase := range testTable {
t.Run(testCase.testName, func(t *testing.T) {
charToString, err := spelling.ConvertCharToName(testCase.givenVal)
if testCase.shouldFail {
if err == nil {
t.Errorf("Character to string conversion succeeded but was expected to fail. Given: %v, returned: %v",
testCase.givenVal, charToString)
}
} else {
if err != nil {
t.Errorf("Character to string conversion failed: %v", err.Error())
}
if charToString != testCase.expVal {
t.Errorf("Character to String conversion fail. Given: %q, expected: %q, got: %q",
testCase.givenVal, testCase.expVal, charToString)
}
}
})
}
t.Run("all_chars_must_return_a_conversion_string", func(t *testing.T) {
cfgObj.UseUpperCase = true
cfgObj.UseLowerCase = true
cfgObj.UseNumber = true
cfgObj.UseSpecial = true
cfgObj.HumanReadable = false
charRange := chars.GetRange(&cfgObj)
for _, curChar := range charRange {
_, err := spelling.ConvertCharToName(byte(curChar))
if err != nil {
t.Fatalf("Character to string conversion failed: %v", err.Error())
}
}
})
t.Run("spell_Ab!_to_strings", func(t *testing.T) {
pwString := "Ab!"
spelledString, err := spelling.String(pwString)
if err != nil {
t.Fatalf("password spelling failed: %v", err.Error())
}
if spelledString != "Alfa/bravo/EXCLAMATION_POINT" {
t.Fatalf(
"Spelling pwString 'Ab!' is expected to provide 'Alfa/bravo/EXCLAMATION_POINT', but returned: %q",
spelledString)
}
})
}
// Benchmark: Random number generation
func BenchmarkGetRandNum(b *testing.B) {
for i := 0; i < b.N; i++ {
_, _ = random.GetNum(100000)
}
}
// Benchmark: Random char generation
func BenchmarkGetRandChar(b *testing.B) {
charRange := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890\"#/!\\$%&+-*.,?=()[]{}:;~^|"
for i := 0; i < b.N; i++ {
_, _ = random.GetChar(charRange, 20)
}
}
// Benchmark: Random char generation
func BenchmarkConvertChar(b *testing.B) {
cfgObj.UseUpperCase = true
cfgObj.UseLowerCase = true
cfgObj.UseNumber = true
cfgObj.UseSpecial = true
cfgObj.HumanReadable = false
charRange := chars.GetRange(&cfgObj)
for i := 0; i < b.N; i++ {
charToConv, _ := random.GetChar(charRange, 1)
charBytes := []byte(charToConv)
_, _ = spelling.ConvertCharToName(charBytes[0])
}
}
// Contains function to search a given slice for values
func containsByte(allowedBytes []int, currentChar int, t *testing.T) bool {
t.Helper()
for _, charInt := range allowedBytes {
if charInt == currentChar {
return true
}
}
return false
}

View file

@ -1,194 +0,0 @@
package config
import (
"flag"
"github.com/wneessen/apg-go/random"
"log"
)
// Config is a struct that holds the different config parameters for the apg-go
// application
type Config struct {
MinPassLen int // Minimum password length
MaxPassLen int // Maximum password length
NumOfPass int // Number of passwords to be generated
useComplex bool // Force complex password generation (implies all other Use* Options to be true)
UseLowerCase bool // Allow lower-case chars in passwords
UseUpperCase bool // Allow upper-case chars in password
UseNumber bool // Allow numbers in passwords
UseSpecial bool // Allow special chars in passwords
HumanReadable bool // Generated passwords use the "human readable" character set
CheckHibp bool // Check generated are validated against the HIBP API for possible leaks
ExcludeChars string // List of characters to be excluded from the PW generation charset
NewStyleModes string // Use the "new style" parameters instead of the single params
spellPassword bool // Spell out passwords in the output
ShowHelp bool // Display the help message in the CLI
ShowVersion bool // Display the version string in the CLI
OutputMode int // Interal parameter to control the output mode of the CLI
PwAlgo int // PW generation algorithm to use (0: random PW based on flags, 1: pronouncable)
SpellPron bool // Spell out the pronouncable password
}
// DefaultMinLength reflects the default minimum length of a generated password
const DefaultMinLength int = 12
// DefaultMaxLength reflects the default maximum length of a generated password
const DefaultMaxLength int = 20
// DefaultPwAlgo reflects the default password generation algorithm
const DefaultPwAlgo int = 1
// New parses the CLI flags and returns a new config object
func New() Config {
var switchConf Config
defaultSwitches := Config{
UseLowerCase: true,
UseUpperCase: true,
UseNumber: true,
UseSpecial: false,
useComplex: false,
HumanReadable: false,
}
config := Config{
UseLowerCase: defaultSwitches.UseLowerCase,
UseUpperCase: defaultSwitches.UseUpperCase,
UseNumber: defaultSwitches.UseNumber,
UseSpecial: defaultSwitches.UseSpecial,
useComplex: defaultSwitches.useComplex,
HumanReadable: defaultSwitches.HumanReadable,
}
// Read and set all flags
flag.BoolVar(&switchConf.UseLowerCase, "L", false, "Use lower case characters in passwords")
flag.BoolVar(&switchConf.UseUpperCase, "U", false, "Use upper case characters in passwords")
flag.BoolVar(&switchConf.UseNumber, "N", false, "Use numerich characters in passwords")
flag.BoolVar(&switchConf.UseSpecial, "S", false, "Use special characters in passwords")
flag.BoolVar(&switchConf.useComplex, "C", false, "Generate complex passwords (implies -L -U -N -S, disables -H)")
flag.BoolVar(&switchConf.HumanReadable, "H", false, "Generate human-readable passwords")
flag.BoolVar(&config.spellPassword, "l", false, "Spell generated password")
flag.BoolVar(&config.CheckHibp, "p", false, "Check the HIBP database if the generated password was leaked before")
flag.BoolVar(&config.ShowVersion, "v", false, "Show version")
flag.IntVar(&config.MinPassLen, "m", DefaultMinLength, "Minimum password length")
flag.IntVar(&config.MaxPassLen, "x", DefaultMaxLength, "Maxiumum password length")
flag.IntVar(&config.NumOfPass, "n", 6, "Number of passwords to generate")
flag.StringVar(&config.ExcludeChars, "E", "", "Exclude list of characters from generated password")
flag.StringVar(&config.NewStyleModes, "M", "",
"New style password parameters (higher priority than single parameters)")
flag.IntVar(&config.PwAlgo, "a", DefaultPwAlgo, "Password generation algorithm")
flag.BoolVar(&config.SpellPron, "t", false, "In pronouncable password mode, spell out the password")
flag.Parse()
// Invert-switch the defaults
if switchConf.UseLowerCase {
config.UseLowerCase = !defaultSwitches.UseLowerCase
}
if switchConf.UseUpperCase {
config.UseUpperCase = !defaultSwitches.UseUpperCase
}
if switchConf.UseNumber {
config.UseNumber = !defaultSwitches.UseNumber
}
if switchConf.UseSpecial {
config.UseSpecial = !defaultSwitches.UseSpecial
}
if switchConf.useComplex {
config.useComplex = !defaultSwitches.useComplex
}
if switchConf.HumanReadable {
config.HumanReadable = !defaultSwitches.HumanReadable
}
// Parse additional parameters and new-style switches
parseParams(&config)
return config
}
// Parse the parameters and set the according config flags
func parseParams(config *Config) {
parseNewStyleParams(config)
// Complex overrides everything
if config.useComplex {
config.UseUpperCase = true
config.UseLowerCase = true
config.UseSpecial = true
config.UseNumber = true
config.HumanReadable = false
}
if !config.UseUpperCase &&
!config.UseLowerCase &&
!config.UseNumber &&
!config.UseSpecial {
log.Fatalf("No password mode set. Cannot generate password from empty character set.")
}
// Set output mode
switch config.PwAlgo {
case 0:
config.OutputMode = 2
default:
config.OutputMode = 0
if config.spellPassword {
config.OutputMode = 1
}
}
}
// Parse the new style parameters
func parseNewStyleParams(config *Config) {
if config.NewStyleModes == "" {
return
}
for _, curParam := range config.NewStyleModes {
switch curParam {
case 'S':
config.UseSpecial = true
case 's':
config.UseSpecial = false
case 'N':
config.UseNumber = true
case 'n':
config.UseNumber = false
case 'L':
config.UseLowerCase = true
case 'l':
config.UseLowerCase = false
case 'U':
config.UseUpperCase = true
case 'u':
config.UseUpperCase = false
case 'H':
config.HumanReadable = true
case 'h':
config.HumanReadable = false
case 'C':
config.useComplex = true
case 'c':
config.useComplex = false
default:
log.Fatalf("Unknown password style parameter: %q\n", string(curParam))
}
}
}
// GetPwLengthFromParams extracts the password length from the given cli flags and stores
// in the provided config object
func GetPwLengthFromParams(config *Config) int {
if config.MinPassLen > config.MaxPassLen {
config.MaxPassLen = config.MinPassLen
}
lenDiff := config.MaxPassLen - config.MinPassLen + 1
randAdd, err := random.GetNum(lenDiff)
if err != nil {
log.Fatalf("Failed to generated password length: %v", err)
}
retVal := config.MinPassLen + randAdd
if retVal <= 0 {
return 1
}
return retVal
}

View file

@ -1 +0,0 @@
apg-go:*:1000:apg-go

View file

@ -1 +0,0 @@
apg-go:*:1000:1000:Automated Password Generator User:/apg-go:/usr/bin/false

View file

@ -1,27 +0,0 @@
package main
import (
"fmt"
"github.com/wneessen/apg-go/chars"
"github.com/wneessen/apg-go/config"
"github.com/wneessen/apg-go/random"
)
func main() {
c := config.Config{
UseNumber: true,
UseSpecial: true,
UseUpperCase: true,
UseLowerCase: true,
PwAlgo: 1,
MinPassLen: 15,
MaxPassLen: 15,
}
pl := config.GetPwLengthFromParams(&c)
cs := chars.GetRange(&c)
pw, err := random.GetChar(cs, pl)
if err != nil {
panic(err)
}
fmt.Println("Your Password:", pw)
}

5
go.mod
View file

@ -1,5 +0,0 @@
module github.com/wneessen/apg-go
go 1.16
require github.com/wneessen/go-hibp v1.0.6

View file

@ -1,80 +0,0 @@
package random
import (
"crypto/rand"
"encoding/binary"
"fmt"
"math/big"
"strings"
)
// Bitmask sizes for the string generators (based on 93 chars total)
const (
letterIdxBits = 7 // 7 bits to represent a letter index
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
)
// GetChar generates random characters based on given character range
// and password length
func GetChar(cr string, l int) (string, error) {
if l < 1 {
return "", fmt.Errorf("length is negative")
}
rs := strings.Builder{}
rs.Grow(l)
crl := len(cr)
rp := make([]byte, 8)
_, err := rand.Read(rp)
if err != nil {
return rs.String(), err
}
for i, c, r := l-1, binary.BigEndian.Uint64(rp), letterIdxMax; i >= 0; {
if r == 0 {
_, err := rand.Read(rp)
if err != nil {
return rs.String(), err
}
c, r = binary.BigEndian.Uint64(rp), letterIdxMax
}
if idx := int(c & letterIdxMask); idx < crl {
rs.WriteByte(cr[idx])
i--
}
c >>= letterIdxBits
r--
}
return rs.String(), nil
}
// GetNum generates a random number with given maximum value
func GetNum(maxNum int) (int, error) {
if maxNum <= 0 {
err := fmt.Errorf("provided maxNum is <= 0: %v", maxNum)
return 0, err
}
maxNumBigInt := big.NewInt(int64(maxNum))
if !maxNumBigInt.IsUint64() {
err := fmt.Errorf("big.NewInt() generation returned negative value: %v", maxNumBigInt)
return 0, err
}
randNum64, err := rand.Int(rand.Reader, maxNumBigInt)
if err != nil {
return 0, err
}
randNum := int(randNum64.Int64())
if randNum < 0 {
err := fmt.Errorf("generated random number does not fit as int64: %v", randNum64)
return 0, err
}
return randNum, nil
}
// CoinFlip performs a simple coinflip based on the rand library and returns true or false
func CoinFlip() bool {
num := big.NewInt(2)
cf, _ := rand.Int(rand.Reader, num)
r := int(cf.Int64())
return r == 1
}

View file

@ -1,140 +0,0 @@
package spelling
import (
"fmt"
"github.com/wneessen/apg-go/chars"
"strings"
)
var (
symbNumNames = map[byte]string{
'1': "ONE",
'2': "TWO",
'3': "THREE",
'4': "FOUR",
'5': "FIVE",
'6': "SIX",
'7': "SEVEN",
'8': "EIGHT",
'9': "NINE",
'0': "ZERO",
33: "EXCLAMATION_POINT",
34: "QUOTATION_MARK",
35: "CROSSHATCH",
36: "DOLLAR_SIGN",
37: "PERCENT_SIGN",
38: "AMPERSAND",
39: "APOSTROPHE",
40: "LEFT_PARENTHESIS",
41: "RIGHT_PARENTHESIS",
42: "ASTERISK",
43: "PLUS_SIGN",
44: "COMMA",
45: "HYPHEN",
46: "PERIOD",
47: "SLASH",
58: "COLON",
59: "SEMICOLON",
60: "LESS_THAN",
61: "EQUAL_SIGN",
62: "GREATER_THAN",
63: "QUESTION_MARK",
64: "AT_SIGN",
91: "LEFT_BRACKET",
92: "BACKSLASH",
93: "RIGHT_BRACKET",
94: "CIRCUMFLEX",
95: "UNDERSCORE",
96: "GRAVE",
123: "LEFT_BRACE",
124: "VERTICAL_BAR",
125: "RIGHT_BRACE",
126: "TILDE",
}
alphabetNames = map[byte]string{
'A': "Alfa",
'B': "Bravo",
'C': "Charlie",
'D': "Delta",
'E': "Echo",
'F': "Foxtrot",
'G': "Golf",
'H': "Hotel",
'I': "India",
'J': "Juliett",
'K': "Kilo",
'L': "Lima",
'M': "Mike",
'N': "November",
'O': "Oscar",
'P': "Papa",
'Q': "Quebec",
'R': "Romeo",
'S': "Sierra",
'T': "Tango",
'U': "Uniform",
'V': "Victor",
'W': "Whiskey",
'X': "X_ray",
'Y': "Yankee",
'Z': "Zulu",
}
)
// String returns an english spelled version of the given string
func String(pwString string) (string, error) {
var returnString []string
for _, curChar := range pwString {
curSpellString, err := ConvertCharToName(byte(curChar))
if err != nil {
return "", err
}
returnString = append(returnString, curSpellString)
}
return strings.Join(returnString, "/"), nil
}
// Koremutake returns the spelling of the Koremutake password with numbers and special
// chars spelled out in english language
func Koremutake(sylList []string) (string, error) {
var returnString []string
for _, curSyl := range sylList {
isKore := false
for _, x := range chars.KoremutakeSyllables {
if x == strings.ToLower(curSyl) {
isKore = true
}
}
if isKore {
returnString = append(returnString, curSyl)
continue
}
curSpellString, err := ConvertCharToName(curSyl[0])
if err != nil {
return "", err
}
returnString = append(returnString, curSpellString)
}
return strings.Join(returnString, "-"), nil
}
// ConvertCharToName converts a given ascii byte into the corresponding english spelled
// name
func ConvertCharToName(charByte byte) (string, error) {
var returnString string
if charByte > 64 && charByte < 91 {
returnString = alphabetNames[charByte]
} else if charByte > 96 && charByte < 123 {
returnString = strings.ToLower(alphabetNames[charByte-32])
} else {
returnString = symbNumNames[charByte]
}
if returnString == "" {
err := fmt.Errorf("cannot convert to character to name: %q is an unknown character", charByte)
return "", err
}
return returnString, nil
}