Merge pull request #32 from wneessen/issue27_pronouncable

Issue 27 -> pronouncable passwords
This commit is contained in:
Winni Neessen 2021-09-23 14:43:55 +02:00 committed by GitHub
commit 9d3417738d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 174 additions and 39 deletions

View file

@ -4,16 +4,28 @@
<option name="autoReloadType" value="ALL" />
</component>
<component name="ChangeListManager">
<list default="true" id="e32960c0-29e5-4669-9fc2-ef12314486ce" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<list default="true" id="e32960c0-29e5-4669-9fc2-ef12314486ce" name="Changes" comment="Added pronounceable passwords #27">
<change beforePath="$PROJECT_DIR$/cmd/apg/apg.go" beforeDir="false" afterPath="$PROJECT_DIR$/cmd/apg/apg.go" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Go File" />
</list>
</option>
</component>
<component name="GOROOT" url="file://$PROJECT_DIR$/../../go1.17.1" />
<component name="Git.Settings">
<option name="RECENT_BRANCH_BY_REPOSITORY">
<map>
<entry key="$PROJECT_DIR$" value="main" />
</map>
</option>
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="GoLibraries">
@ -25,6 +37,7 @@
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">
<property name="DefaultGoTemplateProperty" value="Go File" />
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
<property name="WebServerToolWindowFactoryState" value="true" />
@ -66,6 +79,11 @@
</option>
<option name="oldMeFiltersMigrated" value="true" />
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="IDE settings..." />
<MESSAGE value="Added pronounceable passwords #27" />
<option name="LAST_COMMIT_MESSAGE" value="Added pronounceable passwords #27" />
</component>
<component name="VgoProject">
<integration-enabled>true</integration-enabled>
</component>

22
chars/koremutake.go Normal file
View file

@ -0,0 +1,22 @@
package chars
// KoremutakeSyllables is a slightly modified Koremutake syllabels 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"}

View file

@ -10,6 +10,7 @@ import (
"github.com/wneessen/go-hibp"
"log"
"os"
"strings"
"time"
)
@ -23,6 +24,9 @@ apg [-m <length>] [-x <length>] [-L] [-U] [-N] [-S] [-H] [-C]
[-l] [-M mode] [-E char_string] [-n num_of_pass] [-v] [-h]
Options:
-a ALGORITH Choose the password generation algorithm (Default: 1)
- 0: pronounceable password generation (koremutake syllables)
- 1: random password generation according to password modes/flags
-m LENGTH Minimum length of the password to be generated (Default: 12)
-x LENGTH Maximum length of the password to be generated (Default: 20)
-n NUMBER Amount of password to be generated (Default: 6)
@ -36,7 +40,7 @@ Options:
-C Enable complex password mode (implies -L -U -N -S and disables -H) (Default: off)
-l Spell generated passwords in phonetic alphabet (Default: off)
-p Check the HIBP database if the generated passwords was found in a leak before (Default: off)
'--> this feature requires internet connectivity
- Note: this feature requires internet connectivity
-h Show this help text
-v Show version string`
@ -56,37 +60,80 @@ func main() {
os.Exit(0)
}
// Set PW length and available characterset
charRange := chars.GetRange(&cfgObj)
pwList := make([]string, 0)
sylList := map[string][]string{}
// Generate passwords
for i := 1; i <= cfgObj.NumOfPass; i++ {
// Choose the type of password generation based on the selected algo
for i := 0; i < cfgObj.NumOfPass; i++ {
pwLength := config.GetPwLengthFromParams(&cfgObj)
switch cfgObj.PwAlgo {
case 0:
pwString := ""
pwSyls := make([]string, 0)
charSylSet := chars.KoremutakeSyllables
charSylSet = append(charSylSet,
strings.Split(chars.PwNumbersHuman, "")...)
charSylSet = append(charSylSet,
strings.Split(chars.PwSpecialCharsHuman, "")...)
charSylSetLen := len(charSylSet)
for len(pwString) < pwLength {
randNum, err := random.GetNum(charSylSetLen)
if err != nil {
log.Fatalf("error generating Koremutake syllable: %s", err)
}
nextSyl := charSylSet[randNum]
if random.CoinFlip() {
sylLen := len(nextSyl)
charPos, err := random.GetNum(sylLen)
if err != nil {
log.Fatalf("error generating random number: %s", err)
}
ucChar := string(nextSyl[charPos])
nextSyl = strings.ReplaceAll(nextSyl, ucChar, strings.ToUpper(ucChar))
}
pwString += nextSyl
pwSyls = append(pwSyls, nextSyl)
}
pwList = append(pwList, pwString)
sylList[pwString] = pwSyls
default:
charRange := chars.GetRange(&cfgObj)
pwString, err := random.GetChar(&charRange, pwLength)
if err != nil {
log.Fatalf("error generating random character range: %s\n", err)
}
pwList = append(pwList, pwString)
}
}
for _, p := range pwList {
switch cfgObj.OutputMode {
case 1:
{
spelledPw, err := spelling.String(pwString)
spelledPw, err := spelling.String(p)
if err != nil {
log.Fatalf("error spelling out password: %s\n", err)
}
fmt.Printf("%v (%v)\n", pwString, spelledPw)
break
fmt.Printf("%v (%v)\n", p, spelledPw)
case 2:
fmt.Printf("%s", p)
if cfgObj.SpellPron {
spelledPw, err := spelling.Koremutake(sylList[p])
if err != nil {
log.Fatalf("error spelling out password: %s", err)
}
fmt.Printf(" (%s)", spelledPw)
}
fmt.Println()
default:
{
fmt.Println(pwString)
break
}
fmt.Println(p)
}
if cfgObj.CheckHibp {
hc := hibp.New(hibp.WithHttpTimeout(time.Second*2), hibp.WithPwnedPadding())
pwnObj, _, err := hc.PwnedPassApi.CheckPassword(pwString)
pwnObj, _, err := hc.PwnedPassApi.CheckPassword(p)
if err != nil {
log.Printf("unable to check HIBP database: %v", err)
}

View file

@ -9,22 +9,24 @@ import (
// Config is a struct that holds the different config parameters for the apg-go
// application
type Config struct {
MinPassLen int
MaxPassLen int
NumOfPass int
UseComplex bool
UseLowerCase bool
UseUpperCase bool
UseNumber bool
UseSpecial bool
HumanReadable bool
CheckHibp bool
ExcludeChars string
NewStyleModes string
SpellPassword bool
ShowHelp bool
ShowVersion bool
OutputMode int
MinPassLen int // Minimum password length
MaxPassLen int // Maximum password length
NumOfPass int // Number of passwords to be generated
UseComplex bool // Force complex password generation (implies all other Use* Options to be true)
UseLowerCase bool // Allow lower-case chars in passwords
UseUpperCase bool // Allow upper-case chars in password
UseNumber bool // Allow numbers in passwords
UseSpecial bool // Allow special chars in passwords
HumanReadable bool // Generated passwords use the "human readable" character set
CheckHibp bool // Check generated are validated against the HIBP API for possible leaks
ExcludeChars string // List of characters to be excluded from the PW generation charset
NewStyleModes string // Use the "new style" parameters instead of the single params
SpellPassword bool // Spell out passwords in the output
ShowHelp bool // Display the help message in the CLI
ShowVersion bool // Display the version string in the CLI
OutputMode int // Interal parameter to control the output mode of the CLI
PwAlgo int // PW generation algorithm to use (0: random PW based on flags, 1: pronouncable)
SpellPron bool // Spell out the pronouncable password
}
// DefaultMinLength reflects the default minimum length of a generated password
@ -33,6 +35,9 @@ const DefaultMinLength int = 12
// DefaultMaxLength reflects the default maximum length of a generated password
const DefaultMaxLength int = 20
// DefaultPwAlgo reflects the default password generation algorithm
const DefaultPwAlgo int = 1
// New parses the CLI flags and returns a new config object
func New() Config {
var switchConf Config
@ -69,6 +74,8 @@ func New() Config {
flag.StringVar(&config.ExcludeChars, "E", "", "Exclude list of characters from generated password")
flag.StringVar(&config.NewStyleModes, "M", "",
"New style password parameters (higher priority than single parameters)")
flag.IntVar(&config.PwAlgo, "a", DefaultPwAlgo, "Password generation algorithm")
flag.BoolVar(&config.SpellPron, "t", false, "In pronouncable password mode, spell out the password")
flag.Parse()
// Invert-switch the defaults
@ -118,9 +125,15 @@ func parseParams(config *Config) {
}
// Set output mode
switch config.PwAlgo {
case 0:
config.OutputMode = 2
default:
config.OutputMode = 0
if config.SpellPassword {
config.OutputMode = 1
}
}
}
// Parse the new style parameters

View file

@ -48,3 +48,11 @@ func GetNum(maxNum int) (int, error) {
}
return randNum, nil
}
// CoinFlip performs a simple coinflip based on the rand library and returns true or false
func CoinFlip() bool {
num := big.NewInt(2)
cf, _ := rand.Int(rand.Reader, num)
r := int(cf.Int64())
return r == 1
}

View file

@ -2,6 +2,7 @@ package spelling
import (
"fmt"
"github.com/wneessen/apg-go/chars"
"strings"
)
@ -93,6 +94,32 @@ func String(pwString string) (string, error) {
return strings.Join(returnString, "/"), nil
}
// Koremutake returns the spelling of the Koremutake password with numbers and special
// chars spelled out in english language
func Koremutake(sylList []string) (string, error) {
var returnString []string
for _, curSyl := range sylList {
isKore := false
for _, x := range chars.KoremutakeSyllables {
if x == strings.ToLower(curSyl) {
isKore = true
}
}
if isKore {
returnString = append(returnString, curSyl)
continue
}
curSpellString, err := ConvertCharToName(curSyl[0])
if err != nil {
return "", err
}
returnString = append(returnString, curSpellString)
}
return strings.Join(returnString, "-"), nil
}
// ConvertCharToName converts a given ascii byte into the corresponding english spelled
// name
func ConvertCharToName(charByte byte) (string, error) {