diff --git a/koremutake.go b/koremutake.go new file mode 100644 index 0000000..6af5ed8 --- /dev/null +++ b/koremutake.go @@ -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"} diff --git a/random.go b/random.go index 893339b..8e0b665 100644 --- a/random.go +++ b/random.go @@ -46,6 +46,7 @@ func (g *Generator) CoinFlipBool() bool { func (g *Generator) Generate() (string, error) { switch g.config.Algorithm { case AlgoPronouncable: + return g.generatePronouncable() case AlgoCoinFlip: return g.generateCoinFlip() case AlgoRandom: @@ -56,6 +57,45 @@ func (g *Generator) Generate() (string, error) { 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 // parameters func (g *Generator) GetPasswordLength() (int64, error) { @@ -154,45 +194,14 @@ func (g *Generator) RandomStringFromCharRange(length int64, charRange string) (s return randString.String(), 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() -} - +// checkMinimumRequirements checks if a password meets the minimum requirements specified in the +// generator's configuration. It returns true if the password meets the requirements, otherwise it +// returns false. +// +// The minimum requirements for each character type (lowercase, numeric, special, uppercase) are +// checked independently. For each character type, the corresponding character range is determined +// based on the generator's configuration. The password is then checked for the presence of each +// character in the character range, and a count is maintained. func (g *Generator) checkMinimumRequirements(password string) bool { ok := true if g.config.MinLowerCase > 0 { @@ -275,6 +284,44 @@ func (g *Generator) generateCoinFlip() (string, error) { 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 // to AlgoRandmom func (g *Generator) generateRandom() (string, error) {