A hash cracker for Python 3.0 and up (module) I've worked on pycrack.py to improve it (hopefully). New features:


Renamed to kronos (kronos.py).
Attack modes:


Bruteforce;
Wordlist attack.

Portability:


Kronos can run on Windows, MacOS and Linux, as well as Android (iPhone?).
Kronos can run under any Python 3 installation, not just Python 3.6 or higher.

The full code is now available on GitHub, along with a guide on how to use it.


I'm looking to get some feedback on the current state of the project. What do you think of the setup of classes and functions within the file? Is there a way to significantly shorten the code, without decreasing functionality? -Any other feedback-

GitHub

GitHub guide

I have included a code sample below:

class Bruteforce:
"""Class containing Active and Passive classes,
for verbose and non-verbose bruteforcing.
Class contents:

* class Active
* class Passive
"""
    class Active:
        """Class for active bruteforce attacks, with
        optional debug mode (boolean). Active mode
        should be utilized from the command line or in
        situations where statistical information is required.
        Class contents:

        * __init__() 
        * timer()
        * bruteforce()
        """
        def __init__(self, hash_, algo, charset, min_, max_, debug):
            # Function arguments:

            # hash_: 
                    # [1] UTF-8-encoded hash hashlib.&lt;algo&gt;().hexdigest().
                    # [1] Supported algorithms from 
                    # [1] hashlib.algorithms_available.

                    # [2] A valid file name or valid file path.
                    # [2] The program will treat each new line
                    # [2] as a new hash. Files must be UTF-8 encoded.
                    # [2] Results will be returned as a list.

                    # [3] A list containing one or more hashes.
                    # [3] Results will be returned as a list.

            # algo: Hashlib algorithm (must be available on system).
            # charset: String, can contain "L" / "U" / "D";
                    #  for lowercase, uppercase and digits.
                    #  Example: lower + upper = LU.
            # min_: Minimum length for expected string (any integer);
                  # bruteforce() will echo a warning if min_ &gt; 6,
                  # because of performance.
            # max_: Maximum length for expected string (any integer).
            # debug: Boolean (1 or True for debug mode, else 0 or False);
                  # Debug mode is not recommended due to performance,
                  # use only if absolutely needed.

            self.hash_ = hash_
            self.algo = algo
            self.algo_ = algo       # This retains a copy of algo argument.
            self.charset = charset
            self.min_ = min_
            self.max_ = max_
            self.debug = debug

            if self.min_ &gt; 5 and self.debug:
                print("""{}Warning: minimum length &gt; 5. 
Bruteforcing may take a long time. Consider a wordlist attack.{}"""
                .format(colors['yellow'], colors['none']))

            if path.isfile(self.hash_):
                with open(self.hash_, "r") as infile:
                    hashes = infile.read()
                self.hash_ = hashes.split()

            charset = ""
            charsets = {"L":ascii_lowercase,
                    "U":ascii_uppercase,
                    "D":digits
                   }
            for key in self.charset:
                charset += charsets[key]
            self.charset, charset = charset, None

            self.algo = hashlib.new(self.algo)
This can be shortened:

self.charset = charset
...
charset = ""
charsets = {"L": ascii_lowercase,
            "U": ascii_uppercase,
            "D": digits}
for key in self.charset:
    charset += charsets[key]
self.charset, charset = charset, None


To:

charsets = {"L": ascii_lowercase,
            "U": ascii_uppercase,
            "D": digits}
self.charset = ""
for key in charset:
    self.charset += charsets[key]


The last part can be shortened further by the use of str.join:

self.charset = ''.join(charsets[key] for key in charset)


You might want to wrap this in a try:...except KeyError block for some nice feedback if an unsupported key is supplied, or just leave it as is.

You should also have a look at Python's official style-guide, PEP8. It recommends using a space after the : when defining key: value pairs.