app.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. package main
  2. import (
  3. "flag"
  4. "fmt"
  5. "github.com/trustelem/zxcvbn"
  6. "math"
  7. "os"
  8. "passphrase-entropy/packages/haveibeenpwned"
  9. "strings"
  10. )
  11. func contains(arr [3]string, str string) bool {
  12. for _, a := range arr {
  13. if a == str {
  14. return true
  15. }
  16. }
  17. return false
  18. }
  19. func myUsage() {
  20. fmt.Println("usage: passphrase-entropy <command> [<args>]")
  21. fmt.Println("Available commands are: ")
  22. fmt.Println(" diceware Calculate entropy of a diceware passphrase")
  23. fmt.Println(" random Calculate entropy of a random string")
  24. fmt.Println(" invented Calculate entropy of an invented passphrase")
  25. }
  26. func secondsToHuman(guesses float64) (result string) {
  27. minute := float64(60)
  28. hour := minute * 60
  29. day := hour * 24
  30. month := day * 31
  31. year := month * 12
  32. century := year * 100
  33. if guesses < 1 {
  34. result = "< 1 second"
  35. } else if guesses < minute {
  36. result = fmt.Sprintf("%d seconds", int(math.Round(guesses)))
  37. } else if guesses < hour {
  38. result = fmt.Sprintf("%d minutes", int(math.Round(guesses / minute)))
  39. } else if guesses < day {
  40. result = fmt.Sprintf("%d hours", int(math.Round(guesses / hour)))
  41. } else if guesses < month {
  42. result = fmt.Sprintf("%d days", int(math.Round(guesses / day)))
  43. } else if guesses < year {
  44. result = fmt.Sprintf("%d months", int(math.Round(guesses / month)))
  45. } else if guesses < century {
  46. result = fmt.Sprintf("%d years", int(math.Round(guesses / year)))
  47. } else {
  48. result = fmt.Sprintf("Centuries")
  49. }
  50. return
  51. }
  52. func main() {
  53. var charsets map[string]string
  54. var usedCharsets[] string
  55. var possibleSymbols int = 0
  56. var haveIBeenPwned bool
  57. var passphrase string
  58. var pwnedFlag bool
  59. var err error
  60. charsets = make(map[string]string)
  61. charsets["Numbers"] = "0123456789"
  62. charsets["Lowercase"] = "abcdefghijklmnopqrstuvwxyz"
  63. charsets["Uppercase"] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  64. charsets["Common Special Characters"] = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
  65. charsets["Space"] = " "
  66. charsets["Extended ASCII"] = "€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ"
  67. if len(os.Args) == 1 {
  68. myUsage()
  69. return
  70. }
  71. switch os.Args[1] {
  72. case "diceware":
  73. var words, dictSize int
  74. dicewareCommand := flag.NewFlagSet("diceware", flag.ExitOnError)
  75. dicewareCommand.IntVar(&words,"words", 0, "number of words in passphrase")
  76. dicewareCommand.IntVar(&dictSize,"dictSize", 0, "number of words in dictionary")
  77. dicewareCommand.Parse(os.Args[2:])
  78. if dicewareCommand.Parsed() {
  79. if words == 0 {
  80. dicewareCommand.Usage()
  81. return
  82. }
  83. if dictSize == 0 {
  84. dicewareCommand.Usage()
  85. return
  86. }
  87. entropy := math.Log2(math.Pow(float64(dictSize), float64(words)))
  88. fmt.Println("\nPassphrase entropy:", entropy)
  89. }
  90. case "random":
  91. var pwnedFlag bool
  92. randomCommand := flag.NewFlagSet("random", flag.ExitOnError)
  93. randomCommand.StringVar(&passphrase,"password", "", "the password")
  94. randomCommand.BoolVar(&pwnedFlag, "pwned", false, "check if password has been seen before")
  95. randomCommand.Parse(os.Args[2:])
  96. if randomCommand.Parsed() {
  97. if passphrase == "" {
  98. randomCommand.Usage()
  99. return
  100. }
  101. if pwnedFlag == true {
  102. haveIBeenPwned, err = pwned.IsPasswordCompromised(passphrase)
  103. if haveIBeenPwned == true {
  104. fmt.Println("This password does not have any entropy, because it has been compromised.")
  105. return
  106. }
  107. }
  108. passphraseLength := len(passphrase)
  109. for key, value := range charsets {
  110. if strings.ContainsAny(passphrase, value) {
  111. possibleSymbols += len(value)
  112. usedCharsets = append(usedCharsets, key)
  113. }
  114. }
  115. entropy := math.Log2(math.Pow(float64(possibleSymbols), float64(passphraseLength)))
  116. fmt.Println("\nNumber of characters:", passphraseLength)
  117. fmt.Println("\nUsed character sets:", strings.Join(usedCharsets, ", "))
  118. fmt.Println("\nPassphrase entropy:", entropy)
  119. }
  120. case "invented":
  121. var words string
  122. inventedCommand := flag.NewFlagSet("invented", flag.ExitOnError)
  123. inventedCommand.StringVar(&passphrase,"password", "", "the password")
  124. inventedCommand.StringVar(&words, "words", "", "used words")
  125. inventedCommand.BoolVar(&pwnedFlag, "pwned", false, "check if password has been seen before")
  126. inventedCommand.Parse(os.Args[2:])
  127. if inventedCommand.Parsed() {
  128. if pwnedFlag == true {
  129. haveIBeenPwned, err = pwned.IsPasswordCompromised(passphrase)
  130. }
  131. if err != nil {
  132. fmt.Println("Something went wrong.")
  133. } else if haveIBeenPwned == true {
  134. fmt.Println("This password does not have any entropy, because it has been compromised.")
  135. return
  136. } else {
  137. userInputs := strings.Fields(words)
  138. strength := zxcvbn.PasswordStrength(passphrase, userInputs)
  139. guesses := strength.Guesses
  140. fmt.Printf("\nPassword:\t\t%s\n", passphrase)
  141. fmt.Printf("Password strength:\t%d/4\n", strength.Score)
  142. fmt.Printf("Guesses Log10:\t\t%f\n", math.Log10(float64(guesses)))
  143. fmt.Println("\nGuess times")
  144. fmt.Printf("100 / h:\t\t%s\t(throttled online attack)\n",secondsToHuman(guesses * (3600 / 100)))
  145. fmt.Printf("10 / s:\t\t%s\t(unthrottled online attack)\n",secondsToHuman(guesses/10))
  146. fmt.Printf("10k / s:\t\t%s\t(offline attack, slow hash, many cores)\n",secondsToHuman(guesses / 1e4))
  147. fmt.Printf("10b / s:\t\t%s\t(offline attack, slow hash, many cores)\n",secondsToHuman(guesses / 1e10))
  148. }
  149. }
  150. default:
  151. myUsage()
  152. fmt.Printf("\n%q is not valid command.\n", os.Args[1])
  153. os.Exit(2)
  154. }
  155. }