|
@@ -3,10 +3,11 @@ package main
|
|
|
import (
|
|
|
"flag"
|
|
|
"fmt"
|
|
|
+ "github.com/trustelem/zxcvbn"
|
|
|
"math"
|
|
|
"os"
|
|
|
- "strings"
|
|
|
"passphrase-entropy/packages/haveibeenpwned"
|
|
|
+ "strings"
|
|
|
)
|
|
|
|
|
|
func contains(arr [3]string, str string) bool {
|
|
@@ -26,6 +27,33 @@ func myUsage() {
|
|
|
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
|
|
@@ -33,6 +61,8 @@ func main() {
|
|
|
|
|
|
var haveIBeenPwned bool
|
|
|
var passphrase string
|
|
|
+ var pwnedFlag bool
|
|
|
+
|
|
|
var err error
|
|
|
|
|
|
charsets = make(map[string]string)
|
|
@@ -43,21 +73,6 @@ func main() {
|
|
|
charsets["Space"] = " "
|
|
|
charsets["Extended ASCII"] = "€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ"
|
|
|
|
|
|
- dicewareCommand := flag.NewFlagSet("diceware", flag.ExitOnError)
|
|
|
- dicewarePassphraseFlag := dicewareCommand.String("password", "", "the password")
|
|
|
- wordsFlag := dicewareCommand.Int("words", 0, "number of words in passphrase")
|
|
|
- dictSizeFlag := dicewareCommand.Int("dictSize", 0, "number of words in dictionary")
|
|
|
- delimiterFlag := dicewareCommand.String("delimiter", "", "delimiter used")
|
|
|
-
|
|
|
- randomCommand := flag.NewFlagSet("random", flag.ExitOnError)
|
|
|
- randomPassphraseFlag := randomCommand.String("password", "", "the password")
|
|
|
- randomPwnedFlag := randomCommand.Bool("pwned", false, "check if password has been seen before")
|
|
|
-
|
|
|
- inventedCommand := flag.NewFlagSet("invented", flag.ExitOnError)
|
|
|
- inventedPassphraseFlag := inventedCommand.String("password", "", "the password")
|
|
|
- inventedWords := inventedCommand.Int("words", 0, "number of words in invented passphrase")
|
|
|
- inventedPwnedFlag := inventedCommand.Bool("pwned", false, "check if password has been seen before")
|
|
|
-
|
|
|
if len(os.Args) == 1 {
|
|
|
myUsage()
|
|
|
return
|
|
@@ -65,93 +80,97 @@ func main() {
|
|
|
|
|
|
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:])
|
|
|
- case "invented":
|
|
|
- inventedCommand.Parse(os.Args[2:])
|
|
|
- default:
|
|
|
- myUsage()
|
|
|
- fmt.Printf("\n%q is not valid command.\n", os.Args[1])
|
|
|
- os.Exit(2)
|
|
|
- }
|
|
|
|
|
|
- if *inventedPassphraseFlag != "" {
|
|
|
- passphrase = *inventedPassphraseFlag
|
|
|
- } else if *randomPassphraseFlag != "" {
|
|
|
- passphrase = *randomPassphraseFlag
|
|
|
- } else {
|
|
|
- passphrase = *dicewarePassphraseFlag
|
|
|
- }
|
|
|
+ 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)
|
|
|
|
|
|
- if dicewareCommand.Parsed() {
|
|
|
- if *wordsFlag == 0 {
|
|
|
- dicewareCommand.Usage()
|
|
|
- return
|
|
|
- }
|
|
|
- if *dictSizeFlag == 0 {
|
|
|
- dicewareCommand.Usage()
|
|
|
- return
|
|
|
- }
|
|
|
- if *delimiterFlag != "" {
|
|
|
for key, value := range charsets {
|
|
|
- if strings.ContainsAny(*delimiterFlag, value) {
|
|
|
- *dictSizeFlag += len(value)
|
|
|
+ if strings.ContainsAny(passphrase, value) {
|
|
|
+ possibleSymbols += len(value)
|
|
|
usedCharsets = append(usedCharsets, key)
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
- entropy := math.Log2(math.Pow(float64(*dictSizeFlag), float64(*wordsFlag)))
|
|
|
- fmt.Println("\nPassphrase entropy:", entropy)
|
|
|
- }
|
|
|
-
|
|
|
- if randomCommand.Parsed() {
|
|
|
- if *randomPassphraseFlag == "" {
|
|
|
- randomCommand.Usage()
|
|
|
- return
|
|
|
- }
|
|
|
|
|
|
- if *randomPwnedFlag == 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(*randomPassphraseFlag)
|
|
|
+ entropy := math.Log2(math.Pow(float64(possibleSymbols), float64(passphraseLength)))
|
|
|
|
|
|
- for key, value := range charsets {
|
|
|
- if strings.ContainsAny(*randomPassphraseFlag, value) {
|
|
|
- possibleSymbols += len(value)
|
|
|
- usedCharsets = append(usedCharsets, key)
|
|
|
- }
|
|
|
+ fmt.Println("\nNumber of characters:", passphraseLength)
|
|
|
+ fmt.Println("\nUsed character sets:", strings.Join(usedCharsets, ", "))
|
|
|
+ fmt.Println("\nPassphrase entropy:", entropy)
|
|
|
}
|
|
|
|
|
|
- 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)
|
|
|
- }
|
|
|
-
|
|
|
- if inventedCommand.Parsed() {
|
|
|
+ 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 *inventedWords != 0 {
|
|
|
- inventedCommand.Usage()
|
|
|
- return
|
|
|
- }
|
|
|
+ if inventedCommand.Parsed() {
|
|
|
|
|
|
- if *inventedPwnedFlag == true {
|
|
|
- haveIBeenPwned, err = pwned.IsPasswordCompromised(passphrase)
|
|
|
- }
|
|
|
+ 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 {
|
|
|
- fmt.Println("Calc entropy here")
|
|
|
+ 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)
|
|
|
}
|
|
|
}
|