123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 |
- #!/usr/bin/env python
- #
- # Rough password strength checker, based on entropy calculations.
- # Will not store or reveal the password, as long as 'getpass' is available.
- #
- # What's up with the "passing grades" used in this program? Well, I'm not a
- # cryptographer, but much of the stuff I used comes from this security
- # researcher, who taught me how entropy and search space relate.
- #
- # I understand that stringing dictionary words or other easily-guessable
- # combinations can make brute-forcing easier than it seems, so there's a
- # warning for that. At any rate, I put out the grand entropy total out, so
- # that you can draw up your own conclusions.
- #
- # TODO: currently, only ASCII passwords (passwords you can type in any
- # keyboard) work. Any other character set will be ignored.
- #
- # Copyright (C) 2017 - kzimmermann <https://quitter.se/kzimmermann>
- #
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
- #
- import os
- import sys
- import math
- try:
- from getpass import getpass
- except ImportError:
- print "Error: getpass is not available in this system."
- print "Sorry, for the sake of privacy, we cannot proceed."
- sys.exit(1)
- lowercase = "abcdefgefghijklmnopqrstuvwxyz"
- uppercase = lowercase.upper()
- digits = "0123456789"
- nonalpha = "!@#$%^&*()_+=-{}[]'\\\":;/?.,<>`~ "
- def test_set(string, set):
- '''
- Does the string has any of the characters available in this set?
- '''
- available = False
- for char in string:
- if char in set:
- available = True
- break
- return available
-
- def entropy(string):
- '''
- What is the entropy per character in this string?
- Literally: H = N (log(P,2))
- '''
- has_lower = test_set(string, lowercase)
- has_upper = test_set(string, uppercase)
- has_digit = test_set(string, digits)
- has_other = test_set(string, nonalpha)
-
- return (math.log((
- has_lower * len(lowercase)
- + has_upper * len(uppercase)
- + has_digit * len(digits)
- + has_other * len(nonalpha)), 2)
- )
- def combinate(string):
- return len(string) * entropy(string)
- def test_ent():
- pw = "blabberm0uth!123"
- bits = entropy(pw)
- print "Password '%s' contains about %.2f bits of entropy per char, totalling about %.2f bits of entropy." % (pw, bits, combinate(pw))
- def main():
- test_string = getpass("Enter your password (won't be shown): ")
- total = combinate(test_string)
- if total < 60.0:
- result = "very weak"
- elif total < 80.0:
- result = "weak"
- elif total < 88.0:
- result = "passable"
- elif total < 94.0:
- result = "pretty good"
- else:
- result = "very strong"
- print "Your password contains a total of about %.2f bits of entropy which is %s." % (total, result)
- if total < 80.0:
- 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."
- print "On the other hand, have you considered using a password manager?"
- elif total < 88.0:
- print "It's just enough to survive a trivial attack, but probably not a full offline cracking."
- 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."
- print "Also, have you considered using a password manager?"
- elif total < 94.0:
- print "Congrats, your password is long enough and should survive most offline, indirect cracking attacks, save perhaps for a very advanced actor."
- print "Be careful not to include things like dictionary words or easy repetitions in it. These make guessing it much easier than it seems!"
- elif total >= 94.0:
- print "Congrats, your password is long enough and should survive most offline, indirect cracking attacks, perhaps even by a very advanced actor..."
- 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?)"
- if __name__ == "__main__":
- if "-t" in sys.argv:
- test_ent()
- sys.exit(0)
- main()
|