package main import ( "flag" "fmt" "github.com/trustelem/zxcvbn" "math" "os" "passphrase-entropy/packages/haveibeenpwned" "strings" ) func contains(arr [3]string, str string) bool { for _, a := range arr { if a == str { return true } } return false } func myUsage() { fmt.Println("usage: passphrase-entropy []") fmt.Println("Available commands are: ") fmt.Println(" diceware Calculate entropy of a diceware passphrase") fmt.Println(" random Calculate entropy of a random string") fmt.Println(" invented Calculate entropy of an invented passphrase") } func secondsToHuman(guesses float64) (result string) { minute := float64(60) hour := minute * 60 day := hour * 24 month := day * 31 year := month * 12 century := year * 100 if guesses < 1 { result = "< 1 second" } else if guesses < minute { result = fmt.Sprintf("%d seconds", int(math.Round(guesses))) } else if guesses < hour { result = fmt.Sprintf("%d minutes", int(math.Round(guesses / minute))) } else if guesses < day { result = fmt.Sprintf("%d hours", int(math.Round(guesses / hour))) } else if guesses < month { result = fmt.Sprintf("%d days", int(math.Round(guesses / day))) } else if guesses < year { result = fmt.Sprintf("%d months", int(math.Round(guesses / month))) } else if guesses < century { result = fmt.Sprintf("%d years", int(math.Round(guesses / year))) } else { result = fmt.Sprintf("Centuries") } return } func main() { var charsets map[string]string var usedCharsets[] string var possibleSymbols int = 0 var haveIBeenPwned bool var passphrase string var pwnedFlag bool var err error charsets = make(map[string]string) charsets["Numbers"] = "0123456789" charsets["Lowercase"] = "abcdefghijklmnopqrstuvwxyz" charsets["Uppercase"] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" charsets["Common Special Characters"] = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" charsets["Space"] = " " charsets["Extended ASCII"] = "€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ" if len(os.Args) == 1 { myUsage() return } switch os.Args[1] { case "diceware": var words, dictSize int dicewareCommand := flag.NewFlagSet("diceware", flag.ExitOnError) dicewareCommand.IntVar(&words,"words", 0, "number of words in passphrase") dicewareCommand.IntVar(&dictSize,"dictSize", 0, "number of words in dictionary") dicewareCommand.Parse(os.Args[2:]) if dicewareCommand.Parsed() { if words == 0 { dicewareCommand.Usage() return } if dictSize == 0 { dicewareCommand.Usage() return } entropy := math.Log2(math.Pow(float64(dictSize), float64(words))) fmt.Println("\nPassphrase entropy:", entropy) } case "random": var pwnedFlag bool randomCommand := flag.NewFlagSet("random", flag.ExitOnError) randomCommand.StringVar(&passphrase,"password", "", "the password") randomCommand.BoolVar(&pwnedFlag, "pwned", false, "check if password has been seen before") randomCommand.Parse(os.Args[2:]) if randomCommand.Parsed() { if passphrase == "" { randomCommand.Usage() return } if pwnedFlag == true { haveIBeenPwned, err = pwned.IsPasswordCompromised(passphrase) if haveIBeenPwned == true { fmt.Println("This password does not have any entropy, because it has been compromised.") return } } passphraseLength := len(passphrase) for key, value := range charsets { if strings.ContainsAny(passphrase, value) { possibleSymbols += len(value) usedCharsets = append(usedCharsets, key) } } entropy := math.Log2(math.Pow(float64(possibleSymbols), float64(passphraseLength))) fmt.Println("\nNumber of characters:", passphraseLength) fmt.Println("\nUsed character sets:", strings.Join(usedCharsets, ", ")) fmt.Println("\nPassphrase entropy:", entropy) } case "invented": var words string inventedCommand := flag.NewFlagSet("invented", flag.ExitOnError) inventedCommand.StringVar(&passphrase,"password", "", "the password") inventedCommand.StringVar(&words, "words", "", "used words") inventedCommand.BoolVar(&pwnedFlag, "pwned", false, "check if password has been seen before") inventedCommand.Parse(os.Args[2:]) if inventedCommand.Parsed() { if pwnedFlag == true { haveIBeenPwned, err = pwned.IsPasswordCompromised(passphrase) } if err != nil { fmt.Println("Something went wrong.") } else if haveIBeenPwned == true { fmt.Println("This password does not have any entropy, because it has been compromised.") return } else { userInputs := strings.Fields(words) strength := zxcvbn.PasswordStrength(passphrase, userInputs) guesses := strength.Guesses fmt.Printf("\nPassword:\t\t%s\n", passphrase) fmt.Printf("Password strength:\t%d/4\n", strength.Score) fmt.Printf("Guesses Log10:\t\t%f\n", math.Log10(float64(guesses))) fmt.Println("\nGuess times") fmt.Printf("100 / h:\t\t%s\t(throttled online attack)\n",secondsToHuman(guesses * (3600 / 100))) fmt.Printf("10 / s:\t\t%s\t(unthrottled online attack)\n",secondsToHuman(guesses/10)) fmt.Printf("10k / s:\t\t%s\t(offline attack, slow hash, many cores)\n",secondsToHuman(guesses / 1e4)) fmt.Printf("10b / s:\t\t%s\t(offline attack, slow hash, many cores)\n",secondsToHuman(guesses / 1e10)) } } default: myUsage() fmt.Printf("\n%q is not valid command.\n", os.Args[1]) os.Exit(2) } }