natas11 helper program Intro

I've been having fun with natas a wargame, from overthewire.

To pass a certain stage I had to do some xor'ing, in combination with base64 encoding/decoding. So I wrote a little python utility to help me pass that stage.

Any feedback on the code I've written is helpfull, but I'm especially interested in the repeated_substring() function. I don't think my function is the most optimised way of handling that.

When I run the code it should output the newly generated cookie value:

b'ClVLIh4ASCsCBE8lAxMacFMOXTlTWxooFhRXJh4FGnBTVF4sFxFeLFMK'



Code

import base64

def xor_break_cookie(cookie):
    """Breaking the key"""
    json = b'{"showpassword":"no","bgcolor":"#ffffff"}'
    return ''.join([chr(cookie[i] ^ json[i % len(json)]) for i in range(len(cookie))])

def decode_base64(data):
    """Decoding with the missing padding"""
    missing = len(data) % 4
    if missing != 0:
        data += b'=' * (4 - missing)
    return base64.decodestring(data)

def xor_gen_cookie(key):
    """Getting the new cookie value"""
    json = b'{"showpassword":"yes","bgcolor":"#ffffff"}'
    return ''.join([chr(json[i] ^ key[i % len(key)]) for i in range(len(json))])

def repeated_substring(text):
    """Gets the longest repeated xor key from text"""
    length = x = y = m = 0
    for y in range(len(text)):       
        for x in range(len(text)):     
            substring = text[y:x]
            if len(substring) &gt; length and text.count(substring) &gt;= m and len(substring) &gt; 1:
                m, length, match = text.count(substring), len(substring), substring
    return match

if __name__ == '__main__':
    cookie = decode_base64(b'ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw')
    password = repeated_substring(xor_break_cookie(cookie))
    new_cookie = xor_gen_cookie(bytes(str.encode(password)))
    print(base64.encodestring(bytes(str.encode(new_cookie))).rstrip())
xor_break_cookie is almost exactly the same as xor_gen_cookie. I'd change them to be the same function, xor_repeat, with varing input.
Rather than using a[i] ^ b[i % len(...)] for i in range(len(...)), you can use zip, and itertools.cycle.

v ^ r for v, r in zip(value, cycle(repeater))

Rather than using str.encode('abc'), you can use 'abc'.encode(). It also doesn't make sense to use str.encode, it's un-common to see in Python, and provides no benifit here.
Rather than using bytes('abc'.encode()) you can just use 'abc'.encode().
Rather than ''.join(...).encode(), you can use bytes(...).
In decode_base64, if you merge the modifications of missing into one line you get:

missing = 4 - (len(data) % 4) if (len(data) % 4) != 0 else 0


This can be simplified if you use two modulos, rather than an if. \$(4 - i \% 4) \% 4\$.
This however can be further simplified, if you make the left hand side negative, and so you can use \$-i \% 4\$.

And so I'd use:

def decode_base64(data):
    return base64.decodestring(data + b'=' * (-len(data) % 4))

You can ensure that len(substring) &gt; 1, if you start x at y+1.
It doesn't make sense to me to generate values, and also find the max in the same function. Split them apart.
By splitting the generating code, and the max finding code apart, you can use max:

max(_repeated_substring(text), key=lambda t: (len(t), text.count(t)))

Rather than having \$O(n^3)\$ time complexity, you can get a \$O(n^2)\$ time and \$O(n)\$ space complexity solution by using Counter.


In all I'd use something like the following. I also provide the above two ways to find the maximum:

import base64
from itertools import cycle
from collections import Counter

def xor_repeat(value, repeater):
    return bytes(v ^ r for v, r in zip(value, cycle(repeater)))

def decode_base64(data):
    return base64.decodestring(data + b'=' * (-len(data) % 4))

def _repeated_substring(text):
    for i in range(len(text)):
        for j in range(i+1, len(text)):
            yield text[i:j]

def repeated_substring(text):
    return max(_repeated_substring(text), key=lambda t: (len(t), text.count(t)))

def repeated_substring(text):
    vals = Counter(_repeated_substring(text))
    return max(vals.items(), key=lambda i: (len(i[0]), i[1]))[0]

BREAK_JSON = b'{"showpassword":"no","bgcolor":"#ffffff"}'
GEN_JSON = b'{"showpassword":"yes","bgcolor":"#ffffff"}'

if __name__ == '__main__':
    cookie = decode_base64(b'ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw')
    password = repeated_substring(xor_repeat(cookie, BREAK_JSON))
    new_cookie = xor_repeat(GEN_JSON, password)
    print(base64.encodestring(new_cookie).rstrip())