entrocalc 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. #!/usr/bin/env python
  2. #
  3. # Rough password strength checker, based on entropy calculations.
  4. # Will not store or reveal the password, as long as 'getpass' is available.
  5. #
  6. # What's up with the "passing grades" used in this program? Well, I'm not a
  7. # cryptographer, but much of the stuff I used comes from this security
  8. # researcher, who taught me how entropy and search space relate.
  9. #
  10. # I understand that stringing dictionary words or other easily-guessable
  11. # combinations can make brute-forcing easier than it seems, so there's a
  12. # warning for that. At any rate, I put out the grand entropy total out, so
  13. # that you can draw up your own conclusions.
  14. #
  15. # TODO: currently, only ASCII passwords (passwords you can type in any
  16. # keyboard) work. Any other character set will be ignored.
  17. #
  18. # Copyright (C) 2017 - kzimmermann <https://quitter.se/kzimmermann>
  19. #
  20. # This program is free software: you can redistribute it and/or modify
  21. # it under the terms of the GNU General Public License as published by
  22. # the Free Software Foundation, either version 3 of the License, or
  23. # (at your option) any later version.
  24. #
  25. # This program is distributed in the hope that it will be useful,
  26. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  27. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  28. # GNU General Public License for more details.
  29. #
  30. # You should have received a copy of the GNU General Public License
  31. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  32. #
  33. import os
  34. import sys
  35. import math
  36. try:
  37. from getpass import getpass
  38. except ImportError:
  39. print "Error: getpass is not available in this system."
  40. print "Sorry, for the sake of privacy, we cannot proceed."
  41. sys.exit(1)
  42. lowercase = "abcdefgefghijklmnopqrstuvwxyz"
  43. uppercase = lowercase.upper()
  44. digits = "0123456789"
  45. nonalpha = "!@#$%^&*()_+=-{}[]'\\\":;/?.,<>`~ "
  46. def test_set(string, set):
  47. '''
  48. Does the string has any of the characters available in this set?
  49. '''
  50. available = False
  51. for char in string:
  52. if char in set:
  53. available = True
  54. break
  55. return available
  56. def entropy(string):
  57. '''
  58. What is the entropy per character in this string?
  59. Literally: H = N (log(P,2))
  60. '''
  61. has_lower = test_set(string, lowercase)
  62. has_upper = test_set(string, uppercase)
  63. has_digit = test_set(string, digits)
  64. has_other = test_set(string, nonalpha)
  65. return (math.log((
  66. has_lower * len(lowercase)
  67. + has_upper * len(uppercase)
  68. + has_digit * len(digits)
  69. + has_other * len(nonalpha)), 2)
  70. )
  71. def combinate(string):
  72. return len(string) * entropy(string)
  73. def test_ent():
  74. pw = "blabberm0uth!123"
  75. bits = entropy(pw)
  76. print "Password '%s' contains about %.2f bits of entropy per char, totalling about %.2f bits of entropy." % (pw, bits, combinate(pw))
  77. def main():
  78. test_string = getpass("Enter your password (won't be shown): ")
  79. total = combinate(test_string)
  80. if total < 60.0:
  81. result = "very weak"
  82. elif total < 80.0:
  83. result = "weak"
  84. elif total < 88.0:
  85. result = "passable"
  86. elif total < 94.0:
  87. result = "pretty good"
  88. else:
  89. result = "very strong"
  90. print "Your password contains a total of about %.2f bits of entropy which is %s." % (total, result)
  91. if total < 80.0:
  92. print "More characters, and not character diversity, is usually the best way to strengthen a password. A password of 14 characters, digits and nonalphanumeric included, should be enough."
  93. print "On the other hand, have you considered using a password manager?"
  94. elif total < 88.0:
  95. print "It's just enough to survive a trivial attack, but probably not a full offline cracking."
  96. print "Try adding a service-specific string anywhere in it, this way your password becomes longer, and also unique among the sites you log in."
  97. print "Also, have you considered using a password manager?"
  98. elif total < 94.0:
  99. print "Congrats, your password is long enough and should survive most offline, indirect cracking attacks, save perhaps for a very advanced actor."
  100. print "Be careful not to include things like dictionary words or easy repetitions in it. These make guessing it much easier than it seems!"
  101. elif total >= 94.0:
  102. print "Congrats, your password is long enough and should survive most offline, indirect cracking attacks, perhaps even by a very advanced actor..."
  103. print "... unless, of course, you cheated by using things like dictionary words, repetitions, easy-to-guess numbers, etc. that makes brute-forcing much easier than you might think. (Also, how can you memorize this?)"
  104. if __name__ == "__main__":
  105. if "-t" in sys.argv:
  106. test_ent()
  107. sys.exit(0)
  108. main()