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" /> <option name="autoReloadType" value="ALL" />
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="e32960c0-29e5-4669-9fc2-ef12314486ce" name="Changes" comment=""> <list default="true" id="e32960c0-29e5-4669-9fc2-ef12314486ce" name="Changes" comment="Added pronounceable passwords #27">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/cmd/apg/apg.go" beforeDir="false" afterPath="$PROJECT_DIR$/cmd/apg/apg.go" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" /> <option name="LAST_RESOLUTION" value="IGNORE" />
</component> </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="GOROOT" url="file://$PROJECT_DIR$/../../go1.17.1" />
<component name="Git.Settings"> <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$" /> <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component> </component>
<component name="GoLibraries"> <component name="GoLibraries">
@ -25,6 +37,7 @@
<option name="showLibraryContents" value="true" /> <option name="showLibraryContents" value="true" />
</component> </component>
<component name="PropertiesComponent"> <component name="PropertiesComponent">
<property name="DefaultGoTemplateProperty" value="Go File" />
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" /> <property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" /> <property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
<property name="WebServerToolWindowFactoryState" value="true" /> <property name="WebServerToolWindowFactoryState" value="true" />
@ -66,6 +79,11 @@
</option> </option>
<option name="oldMeFiltersMigrated" value="true" /> <option name="oldMeFiltersMigrated" value="true" />
</component> </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"> <component name="VgoProject">
<integration-enabled>true</integration-enabled> <integration-enabled>true</integration-enabled>
</component> </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" "github.com/wneessen/go-hibp"
"log" "log"
"os" "os"
"strings"
"time" "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] [-l] [-M mode] [-E char_string] [-n num_of_pass] [-v] [-h]
Options: 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) -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)
-n NUMBER Amount of password to be generated (Default: 6) -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) -C Enable complex password mode (implies -L -U -N -S and disables -H) (Default: off)
-l Spell generated passwords in phonetic alphabet (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) -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 -h Show this help text
-v Show version string` -v Show version string`
@ -56,37 +60,80 @@ func main() {
os.Exit(0) os.Exit(0)
} }
// Set PW length and available characterset pwList := make([]string, 0)
charRange := chars.GetRange(&cfgObj) sylList := map[string][]string{}
// Generate passwords // Choose the type of password generation based on the selected algo
for i := 1; i <= cfgObj.NumOfPass; i++ { for i := 0; i < cfgObj.NumOfPass; i++ {
pwLength := config.GetPwLengthFromParams(&cfgObj) 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) pwString, err := random.GetChar(&charRange, pwLength)
if err != nil { if err != nil {
log.Fatalf("error generating random character range: %s\n", err) log.Fatalf("error generating random character range: %s\n", err)
} }
pwList = append(pwList, pwString)
}
}
for _, p := range pwList {
switch cfgObj.OutputMode { switch cfgObj.OutputMode {
case 1: case 1:
{ spelledPw, err := spelling.String(p)
spelledPw, err := spelling.String(pwString)
if err != nil { if err != nil {
log.Fatalf("error spelling out password: %s\n", err) log.Fatalf("error spelling out password: %s\n", err)
} }
fmt.Printf("%v (%v)\n", pwString, spelledPw) fmt.Printf("%v (%v)\n", p, spelledPw)
break 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: default:
{ fmt.Println(p)
fmt.Println(pwString)
break
}
} }
if cfgObj.CheckHibp { if cfgObj.CheckHibp {
hc := hibp.New(hibp.WithHttpTimeout(time.Second*2), hibp.WithPwnedPadding()) 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 { if err != nil {
log.Printf("unable to check HIBP database: %v", err) 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 // Config is a struct that holds the different config parameters for the apg-go
// application // application
type Config struct { type Config struct {
MinPassLen int MinPassLen int // Minimum password length
MaxPassLen int MaxPassLen int // Maximum password length
NumOfPass int NumOfPass int // Number of passwords to be generated
UseComplex bool UseComplex bool // Force complex password generation (implies all other Use* Options to be true)
UseLowerCase bool UseLowerCase bool // Allow lower-case chars in passwords
UseUpperCase bool UseUpperCase bool // Allow upper-case chars in password
UseNumber bool UseNumber bool // Allow numbers in passwords
UseSpecial bool UseSpecial bool // Allow special chars in passwords
HumanReadable bool HumanReadable bool // Generated passwords use the "human readable" character set
CheckHibp bool CheckHibp bool // Check generated are validated against the HIBP API for possible leaks
ExcludeChars string ExcludeChars string // List of characters to be excluded from the PW generation charset
NewStyleModes string NewStyleModes string // Use the "new style" parameters instead of the single params
SpellPassword bool SpellPassword bool // Spell out passwords in the output
ShowHelp bool ShowHelp bool // Display the help message in the CLI
ShowVersion bool ShowVersion bool // Display the version string in the CLI
OutputMode int 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 // 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 // DefaultMaxLength reflects the default maximum length of a generated password
const DefaultMaxLength int = 20 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 // New parses the CLI flags and returns a new config object
func New() Config { func New() Config {
var switchConf 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.ExcludeChars, "E", "", "Exclude list of characters from generated password")
flag.StringVar(&config.NewStyleModes, "M", "", flag.StringVar(&config.NewStyleModes, "M", "",
"New style password parameters (higher priority than single parameters)") "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() flag.Parse()
// Invert-switch the defaults // Invert-switch the defaults
@ -118,10 +125,16 @@ func parseParams(config *Config) {
} }
// Set output mode // Set output mode
switch config.PwAlgo {
case 0:
config.OutputMode = 2
default:
config.OutputMode = 0
if config.SpellPassword { if config.SpellPassword {
config.OutputMode = 1 config.OutputMode = 1
} }
} }
}
// Parse the new style parameters // Parse the new style parameters
func parseNewStyleParams(config *Config) { func parseNewStyleParams(config *Config) {

View file

@ -48,3 +48,11 @@ func GetNum(maxNum int) (int, error) {
} }
return randNum, nil 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 ( import (
"fmt" "fmt"
"github.com/wneessen/apg-go/chars"
"strings" "strings"
) )
@ -93,6 +94,32 @@ func String(pwString string) (string, error) {
return strings.Join(returnString, "/"), nil 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 // ConvertCharToName converts a given ascii byte into the corresponding english spelled
// name // name
func ConvertCharToName(charByte byte) (string, error) { func ConvertCharToName(charByte byte) (string, error) {