mirror of
https://github.com/wneessen/apg-go.git
synced 2024-11-22 05:40:51 +01:00
Implement pronouncable password generation and refactor code
The new function "generatePronouncable" generates pronounceable passwords using the Koremutake syllabic representation. It is executed when 'Generate()' method is called with Algorithm set to 'AlgoPronouncable'. Additionally, significant changes were made to enhance the readability and performance of the 'GetCharRangeFromConfig' and 'checkMinimumRequirements' methods. In 'mode_test.go', the error message format has been updated for clear and concise display.
This commit is contained in:
parent
bd654d40b8
commit
72576961e6
2 changed files with 108 additions and 39 deletions
22
koremutake.go
Normal file
22
koremutake.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package apg
|
||||||
|
|
||||||
|
// 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"}
|
125
random.go
125
random.go
|
@ -46,6 +46,7 @@ func (g *Generator) CoinFlipBool() bool {
|
||||||
func (g *Generator) Generate() (string, error) {
|
func (g *Generator) Generate() (string, error) {
|
||||||
switch g.config.Algorithm {
|
switch g.config.Algorithm {
|
||||||
case AlgoPronouncable:
|
case AlgoPronouncable:
|
||||||
|
return g.generatePronouncable()
|
||||||
case AlgoCoinFlip:
|
case AlgoCoinFlip:
|
||||||
return g.generateCoinFlip()
|
return g.generateCoinFlip()
|
||||||
case AlgoRandom:
|
case AlgoRandom:
|
||||||
|
@ -56,6 +57,45 @@ func (g *Generator) Generate() (string, error) {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCharRangeFromConfig checks the Mode from the Config and returns a
|
||||||
|
// list of all possible characters that are supported by these Mode
|
||||||
|
func (g *Generator) GetCharRangeFromConfig() string {
|
||||||
|
charRange := strings.Builder{}
|
||||||
|
if MaskHasMode(g.config.Mode, ModeLowerCase) {
|
||||||
|
switch MaskHasMode(g.config.Mode, ModeHumanReadable) {
|
||||||
|
case true:
|
||||||
|
charRange.WriteString(CharRangeAlphaLowerHuman)
|
||||||
|
default:
|
||||||
|
charRange.WriteString(CharRangeAlphaLower)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if MaskHasMode(g.config.Mode, ModeNumeric) {
|
||||||
|
switch MaskHasMode(g.config.Mode, ModeHumanReadable) {
|
||||||
|
case true:
|
||||||
|
charRange.WriteString(CharRangeNumericHuman)
|
||||||
|
default:
|
||||||
|
charRange.WriteString(CharRangeNumeric)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if MaskHasMode(g.config.Mode, ModeSpecial) {
|
||||||
|
switch MaskHasMode(g.config.Mode, ModeHumanReadable) {
|
||||||
|
case true:
|
||||||
|
charRange.WriteString(CharRangeSpecialHuman)
|
||||||
|
default:
|
||||||
|
charRange.WriteString(CharRangeSpecial)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if MaskHasMode(g.config.Mode, ModeUpperCase) {
|
||||||
|
switch MaskHasMode(g.config.Mode, ModeHumanReadable) {
|
||||||
|
case true:
|
||||||
|
charRange.WriteString(CharRangeAlphaUpperHuman)
|
||||||
|
default:
|
||||||
|
charRange.WriteString(CharRangeAlphaUpper)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return charRange.String()
|
||||||
|
}
|
||||||
|
|
||||||
// GetPasswordLength returns the password length based on the given config
|
// GetPasswordLength returns the password length based on the given config
|
||||||
// parameters
|
// parameters
|
||||||
func (g *Generator) GetPasswordLength() (int64, error) {
|
func (g *Generator) GetPasswordLength() (int64, error) {
|
||||||
|
@ -154,45 +194,14 @@ func (g *Generator) RandomStringFromCharRange(length int64, charRange string) (s
|
||||||
return randString.String(), nil
|
return randString.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCharRangeFromConfig checks the Mode from the Config and returns a
|
// checkMinimumRequirements checks if a password meets the minimum requirements specified in the
|
||||||
// list of all possible characters that are supported by these Mode
|
// generator's configuration. It returns true if the password meets the requirements, otherwise it
|
||||||
func (g *Generator) GetCharRangeFromConfig() string {
|
// returns false.
|
||||||
charRange := strings.Builder{}
|
//
|
||||||
if MaskHasMode(g.config.Mode, ModeLowerCase) {
|
// The minimum requirements for each character type (lowercase, numeric, special, uppercase) are
|
||||||
switch MaskHasMode(g.config.Mode, ModeHumanReadable) {
|
// checked independently. For each character type, the corresponding character range is determined
|
||||||
case true:
|
// based on the generator's configuration. The password is then checked for the presence of each
|
||||||
charRange.WriteString(CharRangeAlphaLowerHuman)
|
// character in the character range, and a count is maintained.
|
||||||
default:
|
|
||||||
charRange.WriteString(CharRangeAlphaLower)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if MaskHasMode(g.config.Mode, ModeNumeric) {
|
|
||||||
switch MaskHasMode(g.config.Mode, ModeHumanReadable) {
|
|
||||||
case true:
|
|
||||||
charRange.WriteString(CharRangeNumericHuman)
|
|
||||||
default:
|
|
||||||
charRange.WriteString(CharRangeNumeric)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if MaskHasMode(g.config.Mode, ModeSpecial) {
|
|
||||||
switch MaskHasMode(g.config.Mode, ModeHumanReadable) {
|
|
||||||
case true:
|
|
||||||
charRange.WriteString(CharRangeSpecialHuman)
|
|
||||||
default:
|
|
||||||
charRange.WriteString(CharRangeSpecial)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if MaskHasMode(g.config.Mode, ModeUpperCase) {
|
|
||||||
switch MaskHasMode(g.config.Mode, ModeHumanReadable) {
|
|
||||||
case true:
|
|
||||||
charRange.WriteString(CharRangeAlphaUpperHuman)
|
|
||||||
default:
|
|
||||||
charRange.WriteString(CharRangeAlphaUpper)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return charRange.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Generator) checkMinimumRequirements(password string) bool {
|
func (g *Generator) checkMinimumRequirements(password string) bool {
|
||||||
ok := true
|
ok := true
|
||||||
if g.config.MinLowerCase > 0 {
|
if g.config.MinLowerCase > 0 {
|
||||||
|
@ -275,6 +284,44 @@ func (g *Generator) generateCoinFlip() (string, error) {
|
||||||
return "Tails", nil
|
return "Tails", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generatePronouncable is executed when Generate() is called with Algorithm set
|
||||||
|
// to AlgoPronouncable
|
||||||
|
func (g *Generator) generatePronouncable() (string, error) {
|
||||||
|
var password string
|
||||||
|
syllables := make([]string, 0)
|
||||||
|
|
||||||
|
length, err := g.GetPasswordLength()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to calculate password length: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
characterSet := append(KoremutakeSyllables, strings.Split(CharRangeNumericHuman, "")...)
|
||||||
|
characterSet = append(characterSet, strings.Split(CharRangeSpecialHuman, "")...)
|
||||||
|
characterSetLength := len(characterSet)
|
||||||
|
for int64(len(password)) < length {
|
||||||
|
randNum, err := g.RandNum(int64(characterSetLength))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to generate a random number for Koremutake syllable generation: %s",
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
nextSyllable := characterSet[randNum]
|
||||||
|
if g.CoinFlipBool() {
|
||||||
|
syllableLength := len(nextSyllable)
|
||||||
|
characterPosition, err := g.RandNum(int64(syllableLength))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to generate a random number for Koremutake syllable generation: %s",
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
randomChar := string(nextSyllable[characterPosition])
|
||||||
|
nextSyllable = strings.ReplaceAll(nextSyllable, randomChar, strings.ToUpper(randomChar))
|
||||||
|
}
|
||||||
|
password += nextSyllable
|
||||||
|
syllables = append(syllables, nextSyllable)
|
||||||
|
}
|
||||||
|
|
||||||
|
return password, nil
|
||||||
|
}
|
||||||
|
|
||||||
// generateRandom is executed when Generate() is called with Algorithm set
|
// generateRandom is executed when Generate() is called with Algorithm set
|
||||||
// to AlgoRandmom
|
// to AlgoRandmom
|
||||||
func (g *Generator) generateRandom() (string, error) {
|
func (g *Generator) generateRandom() (string, error) {
|
||||||
|
|
Loading…
Reference in a new issue