YotKay
YotKay

Reputation: 1207

Calculate sha256 hash starting with some string based on some fixed data and all permutations of other data

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

Upvotes: 1

Views: 373

Answers (1)

ron
ron

Reputation: 186

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

Upvotes: 1

Related Questions