1

I need to calculate hash which starts with some string. My input is:

  1. other hash (fixed)
  2. some variadic data occupying 5 bytes which I need to prepare in a way so that the result hash starts with e.g. 'ABC'

I am somewhat new in Python, and I came up with this ugly solution (in function calculate). To speed up processing I use executors to run this on all cores.

Can someone help me figure out to make this function 'calculate' less ugly? How to do it better? How to iterate through all permutations of 5 bytes of my variadic data? And finally, what do I do wrong or non-pythonic here?

import hashlib
import concurrent.futures
import copy

input_hash = [0x79,0xaf,0x37,0xc4,0x32,0x8e,0x7b,0x67,0xb1,0xfa,0x76,0x36,0x11,0x21,0xa4,0xdd,0x6c,0x29,0xf0,0x6b,0x50,0x67,0x57,0x16,0x0b,0xee,0x30,0x32,0x2a,0x05,0x9e,0x75]

def calculate(pars):
    print(pars)
    for x in range(pars[0],pars[1]):
        whole = []
        whole.extend(bytearray(input_hash))
        whole.extend(bytes([x]))
        copy1 = copy.deepcopy(whole)
        for y in range(256):
            whole = copy.deepcopy(copy1)
            whole.extend(bytes([y]))
            copy2 = copy.deepcopy(whole)
            for z in range(256):
                whole = copy.deepcopy(copy2)
                whole.extend(bytes([z]))
                copy3 = copy.deepcopy(whole)
                for a in range(256):
                    whole = copy.deepcopy(copy3)
                    whole.extend(bytes([a]))
                    copy4 = copy.deepcopy(whole)
                    for b in range(256):
                        whole = copy.deepcopy(copy4)
                        whole.extend(bytes([b]))
                        whole.extend(bytes([0]*2))
                        m = hashlib.sha256()
                        m.update(bytearray(whole))
                        d = m.hexdigest()
                        if d.startswith('ABC'):
                            print('success!, x = %, y = %, z = %, a = %, b = %' % x, y, z, a, b)
                            return

data = [(0,33),(33,67),(67,101),(101,135),(135,169),(169,203),(203,237),(237,256)]
with concurrent.futures.ProcessPoolExecutor() as executor:
    res = executor.map(calculate, data)
    for r in res:
        pass
YotKay
  • 1,127
  • 1
  • 9
  • 25
  • it looks like your trying to do this https://stackoverflow.com/questions/2898685/hashing-in-sha512-using-a-salt-python – ron Nov 22 '19 at 08:26
  • How about using itertools.permutatoins to generate the permutations? https://docs.python.org/3/library/itertools.html#itertools.permutations – lahsuk Nov 22 '19 at 11:26
  • @ron - your proposal is something different. I calculate hash for all possible values of stored on 5 bytes, prepended by some hash (or salt, if you will, whatever). The core of the problem is to calculate all possible values of 5 bytes and store it in a list, and do it in a nice way (better than my solution above). – YotKay Nov 22 '19 at 16:16
  • Also @lashsuk - I looked at itertools and don't see how I could possilby use it for my case, i.e. the permutate method accepts fixed set of values which it permutates. I would have to provide this list myself, and this is exactly what I need to calculate, not provide hard-coded. – YotKay Nov 22 '19 at 16:16

1 Answers1

1

In order to make your calculate function more readable I would suggest changing the order you do things.
if you calculate all the letters then build your string, the code would look a lot cleaner and more readable. As a byproduct it will run faster to.

Currently you are following this sequence

make string,
append salt
1
copy string
calculate next letter
append letter to string
    2
    copy string
    calculate next letter
    append letter to string
        repeat 3 more time.

        hash string and compare value

it would look cleaner if you

1
calculate next letter
    calculate next letter
        calculate next letter
            ......
            make string
            append salt
            append letters
            hash string and compare value

I follow a simple rule, If I have to write the same instructions more than twice, there a way to simplify the procedure

Your print() statement throws an error when I tried it. I believe your wanting to display the hex results. You will need something like this

print('success!, x = %02x, y = %02x, z = %02x, a = %02x, b = %02x' % (x, y, z, a, b))

There are many other methods to format strings Some can be found here.

You have hard coded an array data[] with 8 values and added a for-range loop to your calculate() function to help split-up the workload for your cpu cores. this limits your code to running on a maximum of 8 cores.

May I suggest letting the ProcessPoolExecutor().map do this for you. it will make more effective use of different hardware setups without you needing knowledge of the systems.

In the calculate() function replace the for x in range(pars[0],pars[1]): with x = pars and correct the indents, then when you call executor.map use a range() like so

res = executor.map(calculate, range(256))

it will pass each process 1 iteration until all are complete. More information about executor.map can be found here.

Here is the code with the above mentioned changes

import hashlib
import concurrent.futures

input_hash = [0x79,0xaf,0x37,0xc4,0x32,0x8e,0x7b,0x67,0xb1,0xfa,0x76,0x36,0x11,0x21,0xa4,0xdd,0x6c,0x29,0xf0,0x6b,0x50,0x67,0x57,0x16,0x0b,0xee,0x30,0x32,0x2a,0x05,0x9e,0x75]

def calculate(pars):
    print(pars)
    x = pars
    for y in range(256):
        for z in range(256):
            for a in range(256):
                for b in range(256):
                    whole = []
                    whole.extend(bytearray(input_hash))
                    whole.extend(bytes([x]))
                    whole.extend(bytes([y]))
                    whole.extend(bytes([z]))
                    whole.extend(bytes([a]))
                    whole.extend(bytes([b]))
                    whole.extend(bytes([0]*2))
                    m = hashlib.sha256()
                    m.update(bytearray(whole))
                    d = m.hexdigest()
                    if d.startswith('ABC'):
                        print('success!, x = %02x, y = %02x, z = %02x, a = %02x, b = %02x' % (x, y, z, a, b))
                        return

with concurrent.futures.ProcessPoolExecutor() as executor:
    res = executor.map(calculate, range(256))
    for r in res:
        pass
ron
  • 186
  • 7
  • Thanks, this looks much cleaner. I have also improved my code myself and used different technique, i.e. instead of a number of nested loops, each running in range of values for single byte, I use single loop iterating through values upto 0x1000000000000 (that is the range for 5 bytes), and I found that method 'to_bytes' can be used on an int object to get array of bytes. This simplifies the program in a great way. But your solution is also very nice and you spotted some other errors and suggested more improvements. Thank you for this! – YotKay Nov 23 '19 at 20:21