4
\$\begingroup\$

I understand my main limitation is my computer's ability to compare millions of combinations but I wondered if there is any way to improve the speed at which the comparisons take place.

My code is as follows:

code = open("C:\\Users\\User\\Desktop\\code.txt").read()
dictionary = open("C:\\Users\\User\\Desktop\\dictionary.txt").read()
precomputed = open("C:\\Users\\User\\Desktop\\precompute.txt").read()
code_found = False
from timeit import default_timer as timer
import itertools
printable = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' #"!"#$%&\'()*+,-./:;<=>?@[\]^_`{|}~'

This sets out the file locations including the file where the 'password' is (named code).

Precomputed is a list of all combinations Printable can make saved to a .txt file:

def bruteforce_by_precomputed():
    code_found = False
    start=timer()
    for attempt in precompute.splitlines():
        if attempt == code:
            print("the word is", attempt)
            code_found = True
            break
        #if attempt != code:
            #print(attempt, "not word")
    if code_found != True:
        print("word not in dictionary") 
    end = timer()
    taken = (end-start)
    print("time taken =  :", taken)

This section is comparing each precomputed value within the .txt file to check if it matches the code:

def dictionary_attack():
    for attempt in dictionary.splitlines():
        if attempt == code:
            print("the word is", attempt)
            code_found = True
            break
        #if attempt != code:
            #print(attempt, "not word")
    if code_found != True:
        print("word not in dictionary")

This section uses a .txt file with every English word for a match:

def infinately_increasing_brute():    
    import itertools
    lengthoforce = 0
    while attempt != code:
        lengthoforce += 1
        for brute_attempt in itertools.product(printable, repeat = lengthoforce):
            attempt = ''.join(brute_attempt)
            if attempt == code:
                print("succesful brute, your word is: ", attempt)
                break

This section uses .product from itertools to find every combination of Printable and if not found increase the length of combination by 1:

def custom_brute():
    lengthoforce = int(input("what length to force?"))
    start = timer()
    for brute_attempt in itertools.product(printable, repeat = lengthoforce):
        attempt = ''.join(brute_attempt)
        if attempt == code:
            print("")
            print("succesful brute, your word is: ", attempt)
            print("")
            end = timer()
            print(end-start, "seconds were taken to find", attempt)
            print("")
            break

This section simply allows the user to enter the length of the combinations to compare.

I have another section of code which creates all possible combinations and saves them to a .txt file. Instead of generating possibilities, it just reads and compares, but I would like to avoid this and generate combinations and compare.

\$\endgroup\$
1
\$\begingroup\$

You should learn about separating concerns. Currently your functions have two concerns, finding the right word and timing how long it takes. You can take out this part by writing a decorator, that allows timing any function you decorate it with.

Next, your bruteforce_by_precomputed and your dictionary_attack are really the same function, just with a different dictionary behind it. You could just let the function accept it as an argument. And let it accept the word do decrypt as well, while we are at it. Normally I would also move the reading of the file here, but since you might want to decrypt more than one word, it could be a good idea to read the files only once.

I would add a maximum length for the infinitely_increasing_brute and just call it brute_force. No point in waiting for the heat death of the universe just to crack that one password...

You should close the files you open. One easy way to do that is to use with.

You should put your main running code under a if __name__ == "__main__ guard. This allows you to easily import the functions defined here in another script, without executing the encryption itself.

You should have a look at the string module, it has many pre-defined sets of characters, including string.ascii_lowercase, string.punctuation and string.printable.

It is not exactly clear if your code file contains only a single word. I modified it so that it can contain many words, separated by newlines.

Putting this together, your code becomes:

import itertools
from time import clock
from string import ascii_lowercase, ascii_uppercase, digits, punctuation

printable = ascii_lowercase + ascii_uppercase + digits


def timeit(func):
    def wrapper(*args, **kwargs):
        starting_time = clock()
        result = func(*args, **kwargs)
        ending_time = clock()
        print('Duration: {}'.format(ending_time - starting_time))
        return result
    return wrapper


@timeit
def dictionary_attack(encrypted, dictionary):
    for attempt in dictionary:
        if attempt == encrypted:
            return attempt


@timeit
def increasing_brute(encrypted, max_len=10):
    for length in range(1, max_len + 1):
        for brute_attempt in itertools.product(printable, repeat=length):
            attempt = ''.join(brute_attempt)
            if attempt == encrypted:
                return attempt


@timeit
def custom_brute(encrypted, length):
    for brute_attempt in itertools.product(printable, repeat=length):
        attempt = ''.join(brute_attempt)
        if attempt == code:
            return attempt


if __name__ == "__main__":
    with open("C:\\Users\\User\\Desktop\\dictionary.txt") as dictionary:
        dictionary_words = dictionary.read().splitlines()
    with open("C:\\Users\\User\\Desktop\\precompute.txt") as precomputed:
        precomputed_words = precomputed.read().splitlines()
    with open("C:\\Users\\User\\Desktop\\code.txt") as code:
        for encrypted in code:
            word = dictionary_attack(encrypted, precomputed_words)
            if word is not None:
                print "The word is", word
                continue
            word = dictionary_attack(encrypted, dictionary_words)
            if word is not None:
                print "The word is", word
                continue
            word = custom_brute(encrypted, int(input("what length to force?")))
            if word is not None:
                print "The word is", word
                continue
            word = increasing_brute(encrpyted)
            if word is not None:
                print "The word is", word
                continue

This could be improved further by making the attack functions generators that yield attempts and a single attack function that performs the comparison using a sequence of functions:

def dictionary_attack(encrypted, dictionary):
    for attempt in dictionary:
        yield attempt


def increasing_brute(encrypted, max_len=10):
    for length in range(1, max_len + 1):
        for brute_attempt in itertools.product(printable, repeat=length):
            yield ''.join(brute_attempt)


def custom_brute(encrypted, *args):
    length = int(input("what length to force?"))
    for brute_attempt in itertools.product(printable, repeat=length):
        yield ''.join(brute_attempt)


@timeit
def attack(encrypted, attack_funcs, attack_args):
    for func, args in zip(attack_funcs, attack_args):
        for attempt in func(encrypted, *args):
            if attempt == encrypted:
                return attempt


if __name__ == "__main__":
    with open("C:\\Users\\User\\Desktop\\dictionary.txt") as dictionary:
        dictionary_words = dictionary.read().splitlines()
    with open("C:\\Users\\User\\Desktop\\precompute.txt") as precomputed:
        precomputed_words = precomputed.read().splitlines()

    attack_funcs = dictionary_attack, dictionary_attack, custom_brute, increasing_brute
    attack_args = [precomputed_words], [dictionary_words], [10], []
    with open("C:\\Users\\User\\Desktop\\code.txt") as code:
        for encrypted in code:
            word = attack(encrypted, attack_funcs, attack_args)
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.