I'm trying to learn more efficient coding so I'm doing some challenges. I have a challenge where I'm given some hex data. I know that hex data is XOR encoded with a four character long ASCII key. The only thing I know about the clear text data is that it contains "a lot of '#' and space characters".
I don't know what they mean by a lot and there are many possibilities that end up with more than 50 hash tags and spaces, though most of them have less than 10 of each. So I figured the only way I could do this would be to narrow down the number of possibilities.
In the following code I'm iterating through every possible combination of 4 character ASCII using the Cartesian product (keys
). I'm using a namedtuple
to store the number of hashtags and spaces for each key that has more than 10 of either. I'm hoping this would help me determine good potential candidates to manually sift through.
import itertools,re,string
from collections import namedtuple
# key is four ascii chars long so we will iterate through data 4-char chunk at a time
data = re.findall('....?', '\x6e\x46\x53\x01\x44\x46\x53\x01\x44\x46\x53\x01\x44\x46\x53\x01\x44\x46\x53\x01\x47\x45\x50\x02\x44\x46\x53\x01\x44\x45\x50\x02\x47\x45\x50\x01\x44\x46\x53\x02\x47\x45\x50\x02\x6e\x46\x53\x01\x44\x46\x53\x01\x44\x46\x53\x01\x44\x46\x53\x01\x44\x46\x53\x01\x47\x45\x53\x01\x47\x45\x53\x01\x44\x45\x50\x01\x44\x46\x53\x01\x44\x46\x50\x02\x44\x46\x53\x01\x6e\x46\x53\x01\x44\x46\x53\x01\x44\x46\x53\x01\x44\x46\x53\x01\x44\x46\x53\x01\x47\x45\x53\x01\x44\x45\x53\x01\x44\x45\x50\x02\x47\x45\x50\x01\x44\x46\x53\x02\x47\x45\x50\x01\x6e\x46\x53\x01\x44\x46\x53\x01\x44\x46\x53\x01\x44\x46\x53\x01\x44\x46\x53\x01\x47\x45\x53\x01\x47\x45\x53\x01\x44\x45\x50\x01\x44\x46\x53\x01\x44\x46\x53\x01\x44\x46\x50\x02\x6e\x46\x53\x01\x44\x46\x53\x01\x44\x46\x53\x01\x44\x46\x53\x01\x44\x46\x53\x01\x47\x45\x50\x02\x44\x46\x53\x01\x44\x45\x50\x01\x44\x46\x53\x01\x44\x46\x50\x02\x47\x45\x50\x01\x6e\x6c\x53\x01\x44\x46\x53\x01\x44\x46\x53\x01\x44\x46\x53\x01\x44\x45\x50\x02\x47\x45\x50\x02\x47\x45\x50\x02\x47\x45\x50\x02\x47\x45\x50\x02\x47\x45\x50\x02\x47\x45\x50\x02\x47\x45\x50\x02\x47\x6c\x53\x01\x44\x46\x53\x01\x44\x46\x53\x01\x44\x46\x53\x01\x44\x45\x50\x01\x44\x46\x30\x4e\x09\x0b\x06\x4f\x0d\x05\x12\x55\x0d\x09\x1d\x52\x44\x25\x1c\x4f\x10\x14\x1c\x4d\x44\x46\x53\x02\x47\x6c\x53\x01\x44\x46\x53\x01\x44\x46\x53\x01\x44\x46\x53\x01\x44\x45\x50\x02\x47\x45\x50\x02\x47\x45\x50\x02\x47\x45\x50\x02\x47\x45\x50\x02\x47\x45\x50\x02\x47\x45\x50\x02\x47\x45\x50\x02\x47\x6c\x79\x6c\x01\x08\x06\x2b\x49\x4b\x5e\x0c\x6e\x6c\x42\x0f\x44\x35\x07\x40\x16\x12\x06\x51\x44\x05\x1c\x4c\x09\x13\x1d\x48\x07\x07\x07\x48\x0b\x08\x00\x0f\x6e\x54\x5d\x01\x37\x0e\x06\x55\x00\x09\x04\x4f\x44\x05\x1c\x4c\x09\x13\x1d\x48\x07\x07\x07\x48\x0b\x08\x00\x0f\x6e\x55\x5d\x01\x28\x09\x14\x4e\x11\x12\x5d\x2b\x6e\x36\x1f\x44\x05\x15\x16\x01\x17\x03\x1f\x44\x07\x12\x53\x58\x0b\x13\x01\x01\x05\x05\x07\x48\x0b\x08\x53\x1f\x6e')
# Cartesian product, all possible printable ascii chars
keys = itertools.product(string.printable, repeat = 4)
# Namedtuple and list used to store good candidates
Possible = namedtuple('Possible', ['key', 'hash', 'space'])
possibles = []
for k in keys:
clear = ''
key = ''.join(x for x in k) # product() returns tuples, need to join them
for word in data:
clear += bytearray(a^b for a, b in zip(*map(bytearray, [word, key])))
# Count hash tags and spaces and store good candidates
hcount = clear.count('#')
scount = clear.count(' ')
if hcount > 10 or scount > 10:
possibles.append( Possible(key, hcount, scount) )
# display candidates
for possible in possibles:
print possible
My code seems to have a very poor performance and I would love to know how I can improve it.