mirror of
https://github.com/wneessen/apg-go.git
synced 2024-11-09 15:52:54 +01:00
Refactor spelling of "Pronounceable" and introduce syllable spelling feature
The spelling of "Pronounceable" has been adjusted throughout the code. Moreover, a new functionality for producing pronounceable passwords spelled as correlating syllables has been integrated. This includes relevant changes to password character sets used for pronounceable passwords and enhancements to test this new feature.
This commit is contained in:
parent
90ff88de41
commit
fefb2557fc
9 changed files with 67 additions and 20 deletions
6
algo.go
6
algo.go
|
@ -5,9 +5,9 @@ package apg
|
||||||
type Algorithm int
|
type Algorithm int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// AlgoPronouncable represents the algorithm for pronouncable passwords
|
// AlgoPronounceable represents the algorithm for pronounceable passwords
|
||||||
// (koremutake syllables)
|
// (koremutake syllables)
|
||||||
AlgoPronouncable Algorithm = iota
|
AlgoPronounceable Algorithm = iota
|
||||||
// AlgoRandom represents the algorithm for purely random passwords according
|
// AlgoRandom represents the algorithm for purely random passwords according
|
||||||
// to the provided password modes/flags
|
// to the provided password modes/flags
|
||||||
AlgoRandom
|
AlgoRandom
|
||||||
|
@ -23,7 +23,7 @@ const (
|
||||||
func IntToAlgo(a int) Algorithm {
|
func IntToAlgo(a int) Algorithm {
|
||||||
switch a {
|
switch a {
|
||||||
case 0:
|
case 0:
|
||||||
return AlgoPronouncable
|
return AlgoPronounceable
|
||||||
case 1:
|
case 1:
|
||||||
return AlgoRandom
|
return AlgoRandom
|
||||||
case 2:
|
case 2:
|
||||||
|
|
|
@ -8,7 +8,7 @@ func TestIntToAlgo(t *testing.T) {
|
||||||
a int
|
a int
|
||||||
e Algorithm
|
e Algorithm
|
||||||
}{
|
}{
|
||||||
{"AlgoPronouncable", 0, AlgoPronouncable},
|
{"AlgoPronounceable", 0, AlgoPronounceable},
|
||||||
{"AlgoRandom", 1, AlgoRandom},
|
{"AlgoRandom", 1, AlgoRandom},
|
||||||
{"AlgoCoinflip", 2, AlgoCoinFlip},
|
{"AlgoCoinflip", 2, AlgoCoinFlip},
|
||||||
{"AlgoUnsupported", 3, AlgoUnsupported},
|
{"AlgoUnsupported", 3, AlgoUnsupported},
|
||||||
|
|
3
apg.go
3
apg.go
|
@ -7,6 +7,9 @@ const VERSION = "2.0.0"
|
||||||
type Generator struct {
|
type Generator struct {
|
||||||
// config is a pointer to the apg config instance
|
// config is a pointer to the apg config instance
|
||||||
config *Config
|
config *Config
|
||||||
|
// syllables holds the single syllables of the lasst generated
|
||||||
|
// pronounceable password
|
||||||
|
syllables []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new password Generator type
|
// New returns a new password Generator type
|
||||||
|
|
|
@ -42,6 +42,7 @@ func main() {
|
||||||
flag.StringVar(&modeString, "M", "", "")
|
flag.StringVar(&modeString, "M", "", "")
|
||||||
flag.Int64Var(&config.NumberPass, "n", config.NumberPass, "")
|
flag.Int64Var(&config.NumberPass, "n", config.NumberPass, "")
|
||||||
flag.BoolVar(&config.SpellPassword, "l", false, "")
|
flag.BoolVar(&config.SpellPassword, "l", false, "")
|
||||||
|
flag.BoolVar(&config.SpellPronounceable, "t", false, "")
|
||||||
flag.Usage = usage
|
flag.Usage = usage
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
@ -114,7 +115,7 @@ func main() {
|
||||||
_, _ = fmt.Fprintf(os.Stderr, "failed to generate password: %s\n", err)
|
_, _ = fmt.Fprintf(os.Stderr, "failed to generate password: %s\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
if config.SpellPassword {
|
if config.Algorithm == apg.AlgoRandom && config.SpellPassword {
|
||||||
spellPass, err := apg.Spell(password)
|
spellPass, err := apg.Spell(password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_, _ = fmt.Fprintf(os.Stderr, "failed to spell password: %s\n", err)
|
_, _ = fmt.Fprintf(os.Stderr, "failed to spell password: %s\n", err)
|
||||||
|
@ -122,6 +123,14 @@ func main() {
|
||||||
fmt.Printf("%s (%s)\n", password, spellPass)
|
fmt.Printf("%s (%s)\n", password, spellPass)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if config.Algorithm == apg.AlgoPronounceable && config.SpellPronounceable {
|
||||||
|
pronouncePass, err := generator.Pronounce()
|
||||||
|
if err != nil {
|
||||||
|
_, _ = fmt.Fprintf(os.Stderr, "failed to pronounce password: %s\n", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("%s (%s)\n", password, pronouncePass)
|
||||||
|
continue
|
||||||
|
}
|
||||||
fmt.Println(password)
|
fmt.Println(password)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,6 +154,8 @@ Flags:
|
||||||
-m LENGTH Minimum length of the password to be generated (Default: 12)
|
-m LENGTH Minimum length of the password to be generated (Default: 12)
|
||||||
-x LENGTH Maximum length of the password to be generated (Default: 20)
|
-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)
|
-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)
|
-n NUMBER Amount of password to be generated (Default: 6)
|
||||||
-E CHARS List of characters to be excluded in the generated password
|
-E CHARS List of characters to be excluded in the generated password
|
||||||
-M [LUNSHClunshc] New style password flags
|
-M [LUNSHClunshc] New style password flags
|
||||||
|
@ -155,6 +166,8 @@ Flags:
|
||||||
-mU NUMBER Minimum amount of upper-case characters (implies -U)
|
-mU NUMBER Minimum amount of upper-case characters (implies -U)
|
||||||
- Note: any of the "Minimum amount of" modes may result in
|
- Note: any of the "Minimum amount of" modes may result in
|
||||||
extraordinarily long calculation times
|
extraordinarily long calculation times
|
||||||
|
- Note 2: The "minimum amount of" modes do not apply in
|
||||||
|
pronounceable mode (-a 0)
|
||||||
-C Enable complex password mode (implies -L -U -N -S and disables -H)
|
-C Enable complex password mode (implies -L -U -N -S and disables -H)
|
||||||
-H Avoid ambiguous characters in passwords (i. e.: 1, l, I, O, 0) (Default: off)
|
-H Avoid ambiguous characters in passwords (i. e.: 1, l, I, O, 0) (Default: off)
|
||||||
-L Toggle lower-case characters in passwords (Default: on)
|
-L Toggle lower-case characters in passwords (Default: on)
|
||||||
|
@ -163,6 +176,8 @@ Flags:
|
||||||
-U Toggle upper-case characters in passwords (Default: on)
|
-U Toggle upper-case characters in passwords (Default: on)
|
||||||
- Note: this flag has higher priority than the other old-style flags
|
- Note: this flag has higher priority than the other old-style flags
|
||||||
-l Spell generated passwords in phonetic alphabet (Default: off)
|
-l Spell generated passwords in phonetic alphabet (Default: off)
|
||||||
|
-t Spell generated pronounceable passwords with the corresponding
|
||||||
|
syllables (Default: off)
|
||||||
-p Check the HIBP database if the generated passwords was found in a leak before (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
|
- Note: this feature requires internet connectivity
|
||||||
-h Show this help text
|
-h Show this help text
|
||||||
|
|
|
@ -43,6 +43,9 @@ type Config struct {
|
||||||
NumberPass int64
|
NumberPass int64
|
||||||
// SpellPassword if set will spell the generated passwords in the phonetic alphabet
|
// SpellPassword if set will spell the generated passwords in the phonetic alphabet
|
||||||
SpellPassword bool
|
SpellPassword bool
|
||||||
|
// SpellPronounceable if set will spell the generated pronounceable passwords in
|
||||||
|
// as its corresponding syllables
|
||||||
|
SpellPronounceable bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option is a function that can override default Config settings
|
// Option is a function that can override default Config settings
|
||||||
|
|
|
@ -35,7 +35,7 @@ func TestWithAlgorithm(t *testing.T) {
|
||||||
algo Algorithm
|
algo Algorithm
|
||||||
want int
|
want int
|
||||||
}{
|
}{
|
||||||
{"Pronouncble passwords", AlgoPronouncable, 0},
|
{"Pronouncble passwords", AlgoPronounceable, 0},
|
||||||
{"Random passwords", AlgoRandom, 1},
|
{"Random passwords", AlgoRandom, 1},
|
||||||
{"Coinflip", AlgoCoinFlip, 2},
|
{"Coinflip", AlgoCoinFlip, 2},
|
||||||
{"Unsupported", AlgoUnsupported, 3},
|
{"Unsupported", AlgoUnsupported, 3},
|
||||||
|
|
17
random.go
17
random.go
|
@ -45,8 +45,8 @@ func (g *Generator) CoinFlipBool() bool {
|
||||||
// it as string type. If the generation fails, an error will be thrown
|
// it as string type. If the generation fails, an error will be thrown
|
||||||
func (g *Generator) Generate() (string, error) {
|
func (g *Generator) Generate() (string, error) {
|
||||||
switch g.config.Algorithm {
|
switch g.config.Algorithm {
|
||||||
case AlgoPronouncable:
|
case AlgoPronounceable:
|
||||||
return g.generatePronouncable()
|
return g.generatePronounceable()
|
||||||
case AlgoCoinFlip:
|
case AlgoCoinFlip:
|
||||||
return g.generateCoinFlip()
|
return g.generateCoinFlip()
|
||||||
case AlgoRandom:
|
case AlgoRandom:
|
||||||
|
@ -284,18 +284,19 @@ func (g *Generator) generateCoinFlip() (string, error) {
|
||||||
return "Tails", nil
|
return "Tails", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// generatePronouncable is executed when Generate() is called with Algorithm set
|
// generatePronounceable is executed when Generate() is called with Algorithm set
|
||||||
// to AlgoPronouncable
|
// to AlgoPronounceable
|
||||||
func (g *Generator) generatePronouncable() (string, error) {
|
func (g *Generator) generatePronounceable() (string, error) {
|
||||||
var password string
|
var password string
|
||||||
syllables := make([]string, 0)
|
g.syllables = make([]string, 0)
|
||||||
|
|
||||||
length, err := g.GetPasswordLength()
|
length, err := g.GetPasswordLength()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to calculate password length: %w", err)
|
return "", fmt.Errorf("failed to calculate password length: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
characterSet := append(KoremutakeSyllables, strings.Split(CharRangeNumericHuman, "")...)
|
characterSet := KoremutakeSyllables
|
||||||
|
characterSet = append(characterSet, strings.Split(CharRangeNumericHuman, "")...)
|
||||||
characterSet = append(characterSet, strings.Split(CharRangeSpecialHuman, "")...)
|
characterSet = append(characterSet, strings.Split(CharRangeSpecialHuman, "")...)
|
||||||
characterSetLength := len(characterSet)
|
characterSetLength := len(characterSet)
|
||||||
for int64(len(password)) < length {
|
for int64(len(password)) < length {
|
||||||
|
@ -316,7 +317,7 @@ func (g *Generator) generatePronouncable() (string, error) {
|
||||||
nextSyllable = strings.ReplaceAll(nextSyllable, randomChar, strings.ToUpper(randomChar))
|
nextSyllable = strings.ReplaceAll(nextSyllable, randomChar, strings.ToUpper(randomChar))
|
||||||
}
|
}
|
||||||
password += nextSyllable
|
password += nextSyllable
|
||||||
syllables = append(syllables, nextSyllable)
|
g.syllables = append(g.syllables, nextSyllable)
|
||||||
}
|
}
|
||||||
|
|
||||||
return password, nil
|
return password, nil
|
||||||
|
|
|
@ -299,14 +299,14 @@ func TestGenerateCoinFlip(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGeneratePronouncable(t *testing.T) {
|
func TestGeneratePronounceable(t *testing.T) {
|
||||||
config := NewConfig()
|
config := NewConfig()
|
||||||
generator := New(config)
|
generator := New(config)
|
||||||
foundSylables := 0
|
foundSylables := 0
|
||||||
for range 100 {
|
for range 100 {
|
||||||
res, err := generator.generatePronouncable()
|
res, err := generator.generatePronounceable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("generatePronouncable() failed: %s", err)
|
t.Errorf("generatePronounceable() failed: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, syl := range KoremutakeSyllables {
|
for _, syl := range KoremutakeSyllables {
|
||||||
|
@ -316,7 +316,7 @@ func TestGeneratePronouncable(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if foundSylables < 100 {
|
if foundSylables < 100 {
|
||||||
t.Errorf("generatePronouncable() failed, expected at least 1 sylable, got none")
|
t.Errorf("generatePronounceable() failed, expected at least 1 sylable, got none")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -440,8 +440,8 @@ func TestGenerate(t *testing.T) {
|
||||||
expectedErr error
|
expectedErr error
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Pronouncable",
|
name: "Pronounceable",
|
||||||
algorithm: AlgoPronouncable,
|
algorithm: AlgoPronounceable,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "CoinFlip",
|
name: "CoinFlip",
|
||||||
|
|
25
spelling.go
25
spelling.go
|
@ -93,6 +93,31 @@ func Spell(input string) (string, error) {
|
||||||
return strings.Join(returnString, "/"), nil
|
return strings.Join(returnString, "/"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pronounce returns last generated pronounceable password as spelled syllables string
|
||||||
|
func (g *Generator) Pronounce() (string, error) {
|
||||||
|
var returnString []string
|
||||||
|
for _, syllable := range g.syllables {
|
||||||
|
isKoremutake := false
|
||||||
|
for _, x := range KoremutakeSyllables {
|
||||||
|
if x == strings.ToLower(syllable) {
|
||||||
|
isKoremutake = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if isKoremutake {
|
||||||
|
returnString = append(returnString, syllable)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
curSpellString, err := ConvertByteToWord(syllable[0])
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
returnString = append(returnString, curSpellString)
|
||||||
|
}
|
||||||
|
return strings.Join(returnString, "-"), nil
|
||||||
|
}
|
||||||
|
|
||||||
// ConvertByteToWord converts a given ASCII byte into the corresponding spelled version
|
// ConvertByteToWord converts a given ASCII byte into the corresponding spelled version
|
||||||
// of the english phonetic alphabet
|
// of the english phonetic alphabet
|
||||||
func ConvertByteToWord(charByte byte) (string, error) {
|
func ConvertByteToWord(charByte byte) (string, error) {
|
||||||
|
|
Loading…
Reference in a new issue