mirror of
https://github.com/wneessen/apg-go.git
synced 2024-11-25 07:10:50 +01:00
Compare commits
33 commits
641af1f88c
...
5ba220f1b9
Author | SHA1 | Date | |
---|---|---|---|
5ba220f1b9 | |||
3ffb499c1b | |||
b40b4b7e63 | |||
f5f6a12e83 | |||
f65feff1f9 | |||
7f8fbb05bc | |||
b289d440da | |||
ef8e334df0 | |||
ba891efd37 | |||
bfc12841ce | |||
4ea41be22f | |||
2691b04e38 | |||
decf5526d1 | |||
061b9f4f7f | |||
bffc8ac65e | |||
6f25663957 | |||
31cf70c678 | |||
4bc210f1ab | |||
b36aeeeab6 | |||
043008a97d | |||
2af31dcb48 | |||
7ebaf2d2b7 | |||
eec1b36edc | |||
2d674214a7 | |||
a61ac9b877 | |||
6697ac53db | |||
64f7eed954 | |||
4a6b9b325f | |||
9f035c5834 | |||
183754e869 | |||
c697a8ef8e | |||
c8a4cf2837 | |||
acadccc84a |
18 changed files with 420 additions and 7 deletions
1
.github/workflows/codecov.yml
vendored
1
.github/workflows/codecov.yml
vendored
|
@ -3,6 +3,7 @@
|
|||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
name: Codecov workflow
|
||||
permissions: read-all
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
|
|
91
.github/workflows/codeql.yml
vendored
Normal file
91
.github/workflows/codeql.yml
vendored
Normal file
|
@ -0,0 +1,91 @@
|
|||
# SPDX-FileCopyrightText: 2022 Winni Neessen <winni@neessen.dev>
|
||||
#
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
schedule:
|
||||
- cron: '31 14 * * 1'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
||||
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
||||
# - https://gh.io/supported-runners-and-hardware-resources
|
||||
# - https://gh.io/using-larger-runners
|
||||
# Consider using larger runners for possible analysis time improvements.
|
||||
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
||||
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
|
||||
permissions:
|
||||
# required for all workflows
|
||||
security-events: write
|
||||
|
||||
# only required for workflows in private repositories
|
||||
actions: read
|
||||
contents: read
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'go' ]
|
||||
# CodeQL supports [ 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' ]
|
||||
# Use only 'java-kotlin' to analyze code written in Java, Kotlin or both
|
||||
# Use only 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
# - name: Autobuild
|
||||
# uses: github/codeql-action/autobuild@v3
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
- run: |
|
||||
echo "Build Application using Go"
|
||||
/usr/bin/env GOTOOLCHAIN=go1.22.1+auto go build -a -installsuffix cgo -ldflags '-w -s -extldflags "-static"' -o apg github.com/wneessen/apg-go/cmd/apg
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
3
.github/workflows/docker-publish.yml
vendored
3
.github/workflows/docker-publish.yml
vendored
|
@ -19,6 +19,9 @@ on:
|
|||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
# Use docker.io for Docker Hub if empty
|
||||
REGISTRY: ghcr.io
|
||||
|
|
2
.github/workflows/reuse.yml
vendored
2
.github/workflows/reuse.yml
vendored
|
@ -3,6 +3,8 @@
|
|||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
name: REUSE Compliance Check
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
|
|
76
.github/workflows/scorecard.yml
vendored
Normal file
76
.github/workflows/scorecard.yml
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
# SPDX-FileCopyrightText: 2022 Winni Neessen <winni@neessen.dev>
|
||||
#
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
# This workflow uses actions that are not certified by GitHub. They are provided
|
||||
# by a third-party and are governed by separate terms of service, privacy
|
||||
# policy, and support documentation.
|
||||
|
||||
name: Scorecard supply-chain security
|
||||
on:
|
||||
# For Branch-Protection check. Only the default branch is supported. See
|
||||
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
|
||||
branch_protection_rule:
|
||||
# To guarantee Maintained check is occasionally updated. See
|
||||
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
|
||||
schedule:
|
||||
- cron: '34 15 * * 4'
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
|
||||
# Declare default permissions as read only.
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
analysis:
|
||||
name: Scorecard analysis
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# Needed to upload the results to code-scanning dashboard.
|
||||
security-events: write
|
||||
# Needed to publish results and get a badge (see publish_results below).
|
||||
id-token: write
|
||||
# Uncomment the permissions below if installing in a private repository.
|
||||
# contents: read
|
||||
# actions: read
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Run analysis"
|
||||
uses: ossf/scorecard-action@e38b1902ae4f44df626f11ba0734b14fb91f8f86 # v2.1.2
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
|
||||
# - you want to enable the Branch-Protection check on a *public* repository, or
|
||||
# - you are installing Scorecard on a *private* repository
|
||||
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
|
||||
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
|
||||
|
||||
# Public repositories:
|
||||
# - Publish results to OpenSSF REST API for easy access by consumers
|
||||
# - Allows the repository to include the Scorecard badge.
|
||||
# - See https://github.com/ossf/scorecard-action#publishing-results.
|
||||
# For private repositories:
|
||||
# - `publish_results` will always be set to `false`, regardless
|
||||
# of the value entered here.
|
||||
publish_results: true
|
||||
|
||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||
# format to the repository Actions tab.
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # v3.1.0
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
retention-days: 5
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2.2.4
|
||||
with:
|
||||
sarif_file: results.sarif
|
1
.github/workflows/sonarqube.yml
vendored
1
.github/workflows/sonarqube.yml
vendored
|
@ -3,6 +3,7 @@
|
|||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
name: SonarQube
|
||||
permissions: read-all
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
|
|
29
README.md
29
README.md
|
@ -49,6 +49,14 @@ vEbErlaFryaNgyex (vE-bEr-la-Fry-aN-gy-ex)
|
|||
We generated a 15-character long pronounceable phrase with syllables output, for easy
|
||||
use in e. g. a phone verification process.
|
||||
|
||||
### Cryptographic key for encryption
|
||||
```shell
|
||||
$ apg -a 3 -f 32 -bh
|
||||
```
|
||||
We generated a 32 bytes/256 bits long fully binary secret that can be used i. e. as
|
||||
encryption key for a AES-256 symmetric encryption. The output is represented in
|
||||
hexadecimal format.
|
||||
|
||||
## Installation
|
||||
|
||||
### Docker
|
||||
|
@ -327,6 +335,24 @@ Heads
|
|||
Heads
|
||||
```
|
||||
|
||||
### Binary mode
|
||||
Since v1.1.0 apg-go has a new algorithm for binary secrets. This is a very basic mode that will ignore
|
||||
most of the available options, as it will only generate binary secrets with full 256 bits of randomness.
|
||||
The only available options for this mode are: `-f` to set the length of the returned secret in bytes,
|
||||
`-bh` to tell apg-go to output the generated secret in hexadecial representation and `-bn` to instruct
|
||||
apg-go to return a newline after the generated secret. Any other option available in the other modes
|
||||
will be ignored.
|
||||
|
||||
This mode can be useful for example if you need to generate a AES-256 encryption key. Since 32 bytes is
|
||||
the default length for the secret generation in this mode, you can simply generate a secret key with
|
||||
the following command:
|
||||
```shell
|
||||
$ apg -a 3 -bh
|
||||
a1cdab8db365af3d70828b1fe43b7896190c157ad3f1ae2a0a1d52ec1628c6b5
|
||||
```
|
||||
*For ease for readability we used the `-bh` flag, to instruct apg-go to output the secret in its
|
||||
hexadecimal representation*
|
||||
|
||||
### Minimum required characters
|
||||
Even though in apg-go you can select what kind of characters are used for the password generation, it is
|
||||
not guaranteed, that if you request a password with a numeric value, that the generated password will
|
||||
|
@ -376,6 +402,9 @@ _apg-go_ replicates most of the parameters of the original c-apg. Some parameter
|
|||
- `0`: Pronouncable password generation (Koremutake syllables)
|
||||
- `1`: Random password generation according to password modes/flags
|
||||
- `2`: Coinflip (returns heads or tails)
|
||||
- `3`: Binary mode (returns a secret with 256 bits of 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)
|
||||
- `-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)
|
||||
- `-f <length>`: Fixed length of the password to be generated (Ignores -m and -x)
|
||||
|
|
38
SECURITY.md
Normal file
38
SECURITY.md
Normal file
|
@ -0,0 +1,38 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2021-2024 Winni Neessen <wn@neessen.dev>
|
||||
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
-->
|
||||
|
||||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
To report (possible) security issues in apg-go, please either send a mail to
|
||||
[security@neessen.dev](mailto:security@neessen.dev) or use Github's
|
||||
[private reporting feature](https://github.com/wneessen/apg-go/security/advisories/new).
|
||||
Reports are always welcome. Even if you are not 100% certain that a specific issue you found
|
||||
counts as a security issue, we'd love to hear the details, so we can figure out together if
|
||||
the issue in question needds to be addressed.
|
||||
|
||||
Typically, you will receive an answer within a day or even within a few hours.
|
||||
|
||||
## Encryption
|
||||
You can send OpenPGP/GPG encrpyted mails to the [security@neessen.dev](mailto:security@neessen.dev) address.
|
||||
|
||||
OpenPGP/GPG public key:
|
||||
```
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
xjMEZfdSjxYJKwYBBAHaRw8BAQdA8YoxV0iaLJxVUkBlpC+FQyOiCvWPcnnk
|
||||
O8rsfRHT22bNK3NlY3VyaXR5QG5lZXNzZW4uZGV2IDxzZWN1cml0eUBuZWVz
|
||||
c2VuLmRldj7CjAQQFgoAPgWCZfdSjwQLCQcICZAajWCli0ncDgMVCAoEFgAC
|
||||
AQIZAQKbAwIeARYhBB6X6h8oUi9vvjcMFxqNYKWLSdwOAACHrQEAmfT2HNXF
|
||||
x1W0z6E6PiuoHDU6DzZ1MC6TZkFfFoC3jJ0BAJZdZnf6xFkVtEAbxNIVpIkI
|
||||
zjVxgI7gefYDXbqzQx4PzjgEZfdSjxIKKwYBBAGXVQEFAQEHQBdOGYxMLrCy
|
||||
+kypzTe9jgaEOjob2VVsZ2UV2K9MGKYYAwEIB8J4BBgWCgAqBYJl91KPCZAa
|
||||
jWCli0ncDgKbDBYhBB6X6h8oUi9vvjcMFxqNYKWLSdwOAABIFAEA3YglATpF
|
||||
YrJxatxHb+yI6WdhhJTA2TaF2bxBl10d/xEA/R5CKbMe3kj647gjiQ1YXQUh
|
||||
dM5AKh9kcJn6FPLEoKEM
|
||||
=nm5C
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
```
|
5
algo.go
5
algo.go
|
@ -18,6 +18,9 @@ const (
|
|||
// AlgoCoinFlip represents a very simple coinflip algorithm returning "heads"
|
||||
// or "tails"
|
||||
AlgoCoinFlip
|
||||
// AlgoBinary represents a full binary randomness mode with up to 256 bits
|
||||
// of randomness
|
||||
AlgoBinary
|
||||
// AlgoUnsupported represents an unsupported algorithm
|
||||
AlgoUnsupported
|
||||
)
|
||||
|
@ -32,6 +35,8 @@ func IntToAlgo(a int) Algorithm {
|
|||
return AlgoRandom
|
||||
case 2:
|
||||
return AlgoCoinFlip
|
||||
case 3:
|
||||
return AlgoBinary
|
||||
default:
|
||||
return AlgoUnsupported
|
||||
}
|
||||
|
|
38
algo_test.go
38
algo_test.go
|
@ -4,7 +4,9 @@
|
|||
|
||||
package apg
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIntToAlgo(t *testing.T) {
|
||||
tt := []struct {
|
||||
|
@ -15,7 +17,8 @@ func TestIntToAlgo(t *testing.T) {
|
|||
{"AlgoPronounceable", 0, AlgoPronounceable},
|
||||
{"AlgoRandom", 1, AlgoRandom},
|
||||
{"AlgoCoinflip", 2, AlgoCoinFlip},
|
||||
{"AlgoUnsupported", 3, AlgoUnsupported},
|
||||
{"AlgoBinary", 3, AlgoBinary},
|
||||
{"AlgoUnsupported", 4, AlgoUnsupported},
|
||||
}
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
@ -26,3 +29,34 @@ func TestIntToAlgo(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzIntToAlgo(f *testing.F) {
|
||||
f.Add(-1) // Test negative input
|
||||
f.Add(4) // Test out-of-range positive input
|
||||
f.Add(100) // Test very large input
|
||||
f.Fuzz(func(t *testing.T, a int) {
|
||||
algo := IntToAlgo(a)
|
||||
switch a {
|
||||
case 0:
|
||||
if algo != AlgoPronounceable {
|
||||
t.Errorf("IntToAlgo(%d) expected AlgoPronounceable, got %v", a, algo)
|
||||
}
|
||||
case 1:
|
||||
if algo != AlgoRandom {
|
||||
t.Errorf("IntToAlgo(%d) expected AlgoRandom, got %v", a, algo)
|
||||
}
|
||||
case 2:
|
||||
if algo != AlgoCoinFlip {
|
||||
t.Errorf("IntToAlgo(%d) expected AlgoCoinFlip, got %v", a, algo)
|
||||
}
|
||||
case 3:
|
||||
if algo != AlgoBinary {
|
||||
t.Errorf("IntToAlgo(%d) expected AlgoBinary, got %v", a, algo)
|
||||
}
|
||||
default:
|
||||
if algo != AlgoUnsupported {
|
||||
t.Errorf("IntToAlgo(%d) expected AlgoUnsupported, got %v", a, algo)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
2
apg.go
2
apg.go
|
@ -5,7 +5,7 @@
|
|||
package apg
|
||||
|
||||
// VERSION represents the version string
|
||||
const VERSION = "1.0.0"
|
||||
const VERSION = "1.1.0"
|
||||
|
||||
// Generator is the password generator type of the APG package
|
||||
type Generator struct {
|
||||
|
|
|
@ -31,6 +31,8 @@ func main() {
|
|||
var modeString string
|
||||
var complexPass, humanReadable, lowerCase, numeric, special, showVer, upperCase bool
|
||||
flag.IntVar(&algorithm, "a", 1, "")
|
||||
flag.BoolVar(&config.BinaryHexMode, "bh", false, "")
|
||||
flag.BoolVar(&config.BinaryNewline, "bn", false, "")
|
||||
flag.BoolVar(&complexPass, "C", false, "")
|
||||
flag.StringVar(&config.ExcludeChars, "E", "", "")
|
||||
flag.Int64Var(&config.FixedLength, "f", 0, "")
|
||||
|
@ -144,6 +146,23 @@ func configOldStyle(config *apg.Config, humanReadable, lowerCase, upperCase,
|
|||
|
||||
func generate(config *apg.Config) {
|
||||
generator := apg.New(config)
|
||||
|
||||
// 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
|
||||
for i := int64(0); i < config.NumberPass; i++ {
|
||||
password, err := generator.Generate()
|
||||
if err != nil {
|
||||
|
@ -197,12 +216,17 @@ Flags:
|
|||
- 0: pronounceable password generation (koremutake syllables)
|
||||
- 1: random password generation according to password modes/flags
|
||||
- 2: coinflip (returns heads or tails)
|
||||
- 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)
|
||||
-m LENGTH Minimum length of the password to be generated (Default: 12)
|
||||
-x LENGTH Maximum length of the password to be generated (Default: 20)
|
||||
-f LENGTH Fixed length of the password to be generated (Ignores -m and -x)
|
||||
- Note: Due to the way the pronounceable password algorithm works,
|
||||
this setting might not always apply
|
||||
-n NUMBER Amount of password to be generated (Default: 6)
|
||||
- Note: Does not apply to binary mode (Algo: 3)
|
||||
-E CHARS List of characters to be excluded in the generated password
|
||||
-M [LUNSHClunshc] New style password flags
|
||||
- Note: new-style flags have higher priority than any of the old-style flags
|
||||
|
|
14
config.go
14
config.go
|
@ -10,6 +10,8 @@ const (
|
|||
DefaultMinLength int64 = 12
|
||||
// DefaultMaxLength reflects the default maximum length of a generated password
|
||||
DefaultMaxLength int64 = 20
|
||||
// DefaultBinarySize is the default byte size for generating binary random bytes
|
||||
DefaultBinarySize int64 = 32
|
||||
// DefaultMode sets the default character set mode bitmask to a combination of
|
||||
// lower- and upper-case characters as well as numbers
|
||||
DefaultMode ModeMask = ModeLowerCase | ModeNumeric | ModeUpperCase
|
||||
|
@ -21,6 +23,11 @@ const (
|
|||
type Config struct {
|
||||
// Algorithm sets the Algorithm used for the password generation
|
||||
Algorithm Algorithm
|
||||
// BinaryHexMode if set will output the hex representation of the generated
|
||||
// binary random string
|
||||
BinaryHexMode bool
|
||||
// BinaryNewline if set will print out a new line in AlgoBinary mode
|
||||
BinaryNewline bool
|
||||
// CheckHIBP sets a flag if the generated password has to be checked
|
||||
// against the HIBP pwned password database
|
||||
CheckHIBP bool
|
||||
|
@ -88,6 +95,13 @@ func WithAlgorithm(algo Algorithm) Option {
|
|||
}
|
||||
}
|
||||
|
||||
// WithBinaryHexMode sets the hex mode for the AlgoBinary
|
||||
func WithBinaryHexMode() Option {
|
||||
return func(config *Config) {
|
||||
config.BinaryHexMode = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithExcludeChars sets a list of characters to be excluded in the generated
|
||||
// passwords
|
||||
func WithExcludeChars(chars string) Option {
|
||||
|
|
|
@ -46,7 +46,8 @@ func TestWithAlgorithm(t *testing.T) {
|
|||
{"Pronounceable passwords", AlgoPronounceable, 0},
|
||||
{"Random passwords", AlgoRandom, 1},
|
||||
{"Coinflip", AlgoCoinFlip, 2},
|
||||
{"Unsupported", AlgoUnsupported, 3},
|
||||
{"Binary", AlgoBinary, 3},
|
||||
{"Unsupported", AlgoUnsupported, 4},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
@ -67,6 +68,18 @@ func TestWithAlgorithm(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestWithBinaryHexMode(t *testing.T) {
|
||||
c := NewConfig(WithBinaryHexMode())
|
||||
if c == nil {
|
||||
t.Errorf("NewConfig(WithBinaryHexMode()) failed, expected config pointer but got nil")
|
||||
return
|
||||
}
|
||||
if !c.BinaryHexMode {
|
||||
t.Errorf("NewConfig(WithBinaryHexMode()) failed, expected chars: %t, got: %t",
|
||||
true, c.BinaryHexMode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithExcludeChars(t *testing.T) {
|
||||
e := "abcdefg"
|
||||
c := NewConfig(WithExcludeChars(e))
|
||||
|
@ -183,3 +196,18 @@ func TestWithModeMask(t *testing.T) {
|
|||
e, c.Mode)
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzWithAlgorithm(f *testing.F) {
|
||||
f.Add(0)
|
||||
f.Add(1)
|
||||
f.Add(2)
|
||||
f.Add(3)
|
||||
f.Add(-1)
|
||||
f.Add(100)
|
||||
f.Fuzz(func(t *testing.T, algo int) {
|
||||
config := NewConfig(WithAlgorithm(Algorithm(algo)))
|
||||
if config.MaxLength < config.MinLength {
|
||||
t.Errorf("Invalid algorithm: %d", algo)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -21,7 +21,8 @@ func TestHasBeenPwned(t *testing.T) {
|
|||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := HasBeenPwned(tt.password)
|
||||
if err != nil {
|
||||
t.Errorf("HasBeenPwned() failed: %s", err)
|
||||
t.Logf("HasBeenPwned() failed: %s", err)
|
||||
return
|
||||
}
|
||||
if tt.want != got {
|
||||
t.Errorf("HasBeenPwned() failed, wanted: %t, got: %t", tt.want, got)
|
||||
|
|
25
random.go
25
random.go
|
@ -57,10 +57,13 @@ func (g *Generator) Generate() (string, error) {
|
|||
return g.generateCoinFlip()
|
||||
case AlgoRandom:
|
||||
return g.generateRandom()
|
||||
case AlgoBinary:
|
||||
return g.generateBinary()
|
||||
case AlgoUnsupported:
|
||||
return "", fmt.Errorf("unsupported algorithm")
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported algorithm")
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// GetCharRangeFromConfig checks the Mode from the Config and returns a
|
||||
|
@ -315,8 +318,26 @@ func (g *Generator) generatePronounceable() (string, error) {
|
|||
return password, nil
|
||||
}
|
||||
|
||||
// generateBinary is executed when Generate() is called with Algorithm set
|
||||
// to AlgoBinary
|
||||
func (g *Generator) generateBinary() (string, error) {
|
||||
length := DefaultBinarySize
|
||||
if g.config.FixedLength > 0 {
|
||||
length = g.config.FixedLength
|
||||
}
|
||||
randBytes := make([]byte, length)
|
||||
_, err := rand.Read(randBytes)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate random bytes: %w", err)
|
||||
}
|
||||
if g.config.BinaryHexMode {
|
||||
return fmt.Sprintf("%x", randBytes), nil
|
||||
}
|
||||
return string(randBytes), nil
|
||||
}
|
||||
|
||||
// generateRandom is executed when Generate() is called with Algorithm set
|
||||
// to AlgoRandmom
|
||||
// to AlgoRandom
|
||||
func (g *Generator) generateRandom() (string, error) {
|
||||
length, err := g.GetPasswordLength()
|
||||
if err != nil {
|
||||
|
|
|
@ -471,6 +471,10 @@ func TestGenerate(t *testing.T) {
|
|||
name: "Random",
|
||||
algorithm: AlgoRandom,
|
||||
},
|
||||
{
|
||||
name: "Binary",
|
||||
algorithm: AlgoBinary,
|
||||
},
|
||||
{
|
||||
name: "Unsupported",
|
||||
algorithm: AlgoUnsupported,
|
||||
|
@ -530,3 +534,38 @@ func BenchmarkGenerator_RandomString(b *testing.B) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerator_generateBinary(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config *Config
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Positive Case - Binary in Hex",
|
||||
config: &Config{FixedLength: 10, BinaryHexMode: true},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Negative Case - Insufficient length",
|
||||
config: &Config{FixedLength: -1, BinaryHexMode: false},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Positive Case - Binary without Hex",
|
||||
config: &Config{FixedLength: 10, BinaryHexMode: false},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := &Generator{config: tt.config}
|
||||
_, err := g.generateBinary()
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Generator.generateBinary() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,6 +140,12 @@ func TestPronounce(t *testing.T) {
|
|||
want: "mu-sa",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Pronounce_Mixed",
|
||||
syllables: []string{"mu", "1"},
|
||||
want: "mu-ONE",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Pronounce_NonKoremutakeSyllable",
|
||||
syllables: []string{"ä"},
|
||||
|
|
Loading…
Reference in a new issue