diff --git a/homework05/hulk.py b/homework05/hulk.py old mode 100644 new mode 100755 index 89aa887..a89155f --- a/homework05/hulk.py +++ b/homework05/hulk.py @@ -29,9 +29,9 @@ def sha1sum(s: str) -> str: >>> sha1sum('a') '86f7e437faa5a7fce15d1ddcb9eaeaea377667b8' ''' - # TODO: Use the hashlib library to produce the SHA1 hex digest of the given - # string. - return '' + hashOfString = hashlib.sha1() + hashOfString.update(s.encode('utf-8')) + return hashOfString.hexdigest() def permutations(length: int, alphabet: str=ALPHABET) -> Iterator[str]: ''' Recursively yield all permutations of alphabet up to given length. @@ -42,9 +42,12 @@ def permutations(length: int, alphabet: str=ALPHABET) -> Iterator[str]: ba bb ''' - # TODO: Use yield to create a generator function that recursively produces - # all the permutations of the given alphabet up to the provided length. - yield '' + if length == 0: # Base case + yield '' + else: # Recursive case + for prefix in alphabet: + for suffix in permutations(length - 1, alphabet): + yield prefix + suffix # this can be done in one line but I think this is more readble def flatten(sequence: Iterable[Iterable[str]]) -> Iterator[str]: ''' Flatten sequence of iterables. @@ -55,8 +58,10 @@ def flatten(sequence: Iterable[Iterable[str]]) -> Iterator[str]: c d ''' - # TODO: Iterate through sequence and yield from each iterator in sequence. - yield '' + for sub_iterable in sequence: + yield from sub_iterable + + def crack(hashes: set[str], length: int, alphabet: str=ALPHABET, prefix: str='') -> list[str]: ''' Return all password permutations of specified length that are in hashes @@ -67,10 +72,14 @@ def crack(hashes: set[str], length: int, alphabet: str=ALPHABET, prefix: str='') b c ''' - # TODO: Return list comprehension that iterates over a sequence of - # candidate permutations and checks if the sha1sum of each candidate is in - # hashes. - return [] + matches = [] + for perm in permutations(length, alphabet): + candidate = prefix + perm + if sha1sum(candidate) in hashes: + matches.append(candidate) + return matches + + def whack(arguments: tuple[set[str], int, str, str]) -> list[str]: ''' Call the crack function with the specified list of arguments @@ -80,7 +89,8 @@ def whack(arguments: tuple[set[str], int, str, str]) -> list[str]: b c ''' - return [] + hashes, length, alphabet, prefix = arguments + return crack(hashes, length, alphabet, prefix) def smash(hashes: set[str], length: int, alphabet: str=ALPHABET, prefix: str='', cores: int=1) -> Iterator[str]: ''' Return all password permutations of specified length that are in hashes @@ -91,9 +101,15 @@ def smash(hashes: set[str], length: int, alphabet: str=ALPHABET, prefix: str='', b c ''' - # TODO: Create generator expression with arguments to pass to whack and - # then use ProcessPoolExecutor to apply whack to all items in expression. - yield '' + arguments = ((hashes, length-1, alphabet, prefix + p) for p in alphabet) + + # Use the ProcessPoolExecutor to make use of mutliple cores (specified by user) + with concurrent.futures.ProcessPoolExecutor(cores) as executor: + results = executor.map(whack, arguments) + + return flatten(results) # return results after flattening them + + # Main Execution @@ -112,13 +128,44 @@ def main(arguments: list[str]=sys.argv[1:]) -> None: length = 1 prefix = '' - # TODO: Parse command line arguments + # Parse command line arguments (I use the same while loop structure as searx.py) + i = 0 + while i < len(arguments): + arg = arguments[i] - # TODO: Load hashes set + if arg == '-a': + alphabet = arguments[i+1] + i += 2 + elif arg == '-c': + cores = int(arguments[i+1]) + i += 2 + elif arg == '-l': + length = int(arguments[i+1]) + i += 2 + elif arg == '-p': + prefix = arguments[i+1] + i += 2 + elif arg == '-s': + hashes_path = arguments[i+1] + i += 2 + elif arg == '-h': + usage(0) + else: + usage(1) - # TODO: Execute smash function + # Load hashes set + hashes = set() + with open(hashes_path) as hashfile: + for line in hashfile: + hashes.add(line.strip()) + + # Execute smash function and store results + results = smash(hashes, length, alphabet, prefix, cores) + + # Print all found passwords + for password in results: + print(password) - # TODO: Print all found passwords if __name__ == '__main__': main()