Compare commits

..

No commits in common. "5ba220f1b9524044c4f41ea163f8bfc93ab10b89" and "641af1f88cfd04190dec19c91547c502483bf924" have entirely different histories.

18 changed files with 7 additions and 420 deletions

View file

@ -3,7 +3,6 @@
# SPDX-License-Identifier: CC0-1.0
name: Codecov workflow
permissions: read-all
on:
push:
branches:

View file

@ -1,91 +0,0 @@
# 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}}"

View file

@ -19,9 +19,6 @@ on:
pull_request:
branches: [ main ]
permissions:
contents: read
env:
# Use docker.io for Docker Hub if empty
REGISTRY: ghcr.io

View file

@ -3,8 +3,6 @@
# SPDX-License-Identifier: CC0-1.0
name: REUSE Compliance Check
permissions:
contents: read
on: [push, pull_request]

View file

@ -1,76 +0,0 @@
# 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

View file

@ -3,7 +3,6 @@
# SPDX-License-Identifier: CC0-1.0
name: SonarQube
permissions: read-all
on:
push:
branches:

View file

@ -49,14 +49,6 @@ 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
@ -335,24 +327,6 @@ 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
@ -402,9 +376,6 @@ _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)

View file

@ -1,38 +0,0 @@
<!--
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-----
```

View file

@ -18,9 +18,6 @@ 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
)
@ -35,8 +32,6 @@ func IntToAlgo(a int) Algorithm {
return AlgoRandom
case 2:
return AlgoCoinFlip
case 3:
return AlgoBinary
default:
return AlgoUnsupported
}

View file

@ -4,9 +4,7 @@
package apg
import (
"testing"
)
import "testing"
func TestIntToAlgo(t *testing.T) {
tt := []struct {
@ -17,8 +15,7 @@ func TestIntToAlgo(t *testing.T) {
{"AlgoPronounceable", 0, AlgoPronounceable},
{"AlgoRandom", 1, AlgoRandom},
{"AlgoCoinflip", 2, AlgoCoinFlip},
{"AlgoBinary", 3, AlgoBinary},
{"AlgoUnsupported", 4, AlgoUnsupported},
{"AlgoUnsupported", 3, AlgoUnsupported},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
@ -29,34 +26,3 @@ 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
View file

@ -5,7 +5,7 @@
package apg
// VERSION represents the version string
const VERSION = "1.1.0"
const VERSION = "1.0.0"
// Generator is the password generator type of the APG package
type Generator struct {

View file

@ -31,8 +31,6 @@ 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, "")
@ -146,23 +144,6 @@ 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 {
@ -216,17 +197,12 @@ 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

View file

@ -10,8 +10,6 @@ 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
@ -23,11 +21,6 @@ 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
@ -95,13 +88,6 @@ 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 {

View file

@ -46,8 +46,7 @@ func TestWithAlgorithm(t *testing.T) {
{"Pronounceable passwords", AlgoPronounceable, 0},
{"Random passwords", AlgoRandom, 1},
{"Coinflip", AlgoCoinFlip, 2},
{"Binary", AlgoBinary, 3},
{"Unsupported", AlgoUnsupported, 4},
{"Unsupported", AlgoUnsupported, 3},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@ -68,18 +67,6 @@ 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))
@ -196,18 +183,3 @@ 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)
}
})
}

View file

@ -21,8 +21,7 @@ func TestHasBeenPwned(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
got, err := HasBeenPwned(tt.password)
if err != nil {
t.Logf("HasBeenPwned() failed: %s", err)
return
t.Errorf("HasBeenPwned() failed: %s", err)
}
if tt.want != got {
t.Errorf("HasBeenPwned() failed, wanted: %t, got: %t", tt.want, got)

View file

@ -57,13 +57,10 @@ 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
@ -318,26 +315,8 @@ 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 AlgoRandom
// to AlgoRandmom
func (g *Generator) generateRandom() (string, error) {
length, err := g.GetPasswordLength()
if err != nil {

View file

@ -471,10 +471,6 @@ func TestGenerate(t *testing.T) {
name: "Random",
algorithm: AlgoRandom,
},
{
name: "Binary",
algorithm: AlgoBinary,
},
{
name: "Unsupported",
algorithm: AlgoUnsupported,
@ -534,38 +530,3 @@ 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
}
})
}
}

View file

@ -140,12 +140,6 @@ 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{"ä"},