# CHALLENGE
A stream cipher in only 122 bytes!Note: This has been tested on python versions 3.8 and 3.9
# WRITEUP
In this challenge we are given a python script `mingen.py` that produced an output `output.txt`The script is written in only two lines, but takes a complex analysis. I will try to break it down in simple terms:
```Pythonexec('def f(x):'+'yield((x:=-~x)*x+-~-x)%727;'*100)g=f(id(f));print(*map(lambda c:ord(c)^next(g),list(open('f').read())))```The first line of the code is declaring a function 'f' taking an argument 'x' `def f(x)` which does some crazy math with you input 100 times. Therefore, it generates a 100-byte long generator item. In simple terms, you can compare this to a list of 100 items.Then, the function is called using `id(f)` as parameter and stored in the variable `g`. In simple terms, the function `id()` function returns the identity of an object. You could say that this is "random", since everytime you execute this script it will generate a different number.
So now we know that we are inputing a "random" number as x to `f(x)`, some black magic happens 100 times, and this "list" (generator) is stored in the variable `g`.However, when you try to `print(g)`, you get something like `<generator object f at 0x7f834f0d3660>`. In order see the actual numbeers, we need to run `print(*g)`, this is because the `*` collects all the positional arguments in a tuple, similar how `for item in items` would collect every item in a list.
Let's move on with the code analysis:this big part: `print(*map(lambda c:ord(c)^next(g),list(open('f').read())))` is doing the following:1. opens the file "f" (we assume it's the flag file), reads it, and returns it as a list, so that every character of the file is now a list item.2. stores the flag in the `c` variable, which is done using `lambda c`.3. xor each flag character with each number in the `g` variable.
I'll rewrite the whole code now in simple terms:```Pythondef f(x):	my_list = []	for i in range(100):		x = ((-~x)*x+-~-x)%727 #I didn't actually verify if this works, think of as pseudo-code just to give a basic idea of what's happening		my_list.append(x)	return my_list
flag = open('f').read()output = []random_number = id(f)g = f(random_number)
for i in range(len(flag)):	output.append(ord(flag[i]) ^ g[i])
print(output) ```Now, the thing about xor encryption is that it can be easily reversed. This is because of the following:a ^ b = cc ^ a = bc ^ b = a
We already know our c, because that's what's in the `output.txt` file given.We know that a is supposed to be the real flag. The real flag always starts with `rarctf{`We know that b is the generator object stored in the `g` variable.
So we can take the first 7 elements of our `output.txt` and xor with `rarctf{`. Hence c ^ a = b.
```Pythonoutput = [281, 547, 54, 380, 392, 98, 158, 440, 724, 218, 406, 672, 193, 457, 694, 208, 455, 745, 196, 450, 724]flag = 'rarctf{'
print("xor result of flag and output:")for i in range(len(flag)):	print(ord(flag[i]) ^ output[i], end = ' ')```
result = 363 578 68 287 508 4 229
Now let's brute force the `f(x)` to see what input we need to return a sequence that starts in those numbers:
```Pythonprint("number for x that will have g start in 363")for i in range(1000):	g = f(i)	if next(g) == 363:		print(i, end = ' ')```result = 256 470 983 
So these numbers will generate a sequence that starts in 363, just like our "c ^ a" result above. One of these numbers might generate a sequence that's identical to what we're looking for. Let's test them:```Pythonx = 256print(f"Next 6 digits in g for x starting in {x}")g = f(x)for i in range(7):	print(next(g), end = ' ')```result = 363 150 666 457 250 45 569 Not what we're looking for, let's move on:
```Pythonx = 470print(f"Next 6 digits in g for x starting in {x}")g = f(x)for i in range(7):	print(next(g), end = ' ')```result = 363 578 68 287 508 4 229 Bingo!This matches the result of output ^ flagNow that we know that `470` will generate the list we want, we only need to xor it with the output to get the flag:```Pythong = f(x)print("xor output with g to get the flag")for n in output:	print(chr(n ^ next(g)), end = '')```result = **rarctf{pyg01f_1s_fun}**
Full code:```Pythonexec('def f(x):'+'yield((x:=-~x)*x+-~-x)%727;'*100)
print("number for x that will have g start in 363")for i in range(1000):	g = f(i)	if next(g) == 363:		print(i, end = ' ')print("\n")
x = 470print(f"Next 6 digits in g for x starting in {x}")g = f(x)for i in range(7):	print(next(g), end = ' ')print("\n")
output = [281, 547, 54, 380, 392, 98, 158, 440, 724, 218, 406, 672, 193, 457, 694, 208, 455, 745, 196, 450, 724]flag = 'rarctf{'
print("xor result of flag and output:")for i in range(len(flag)):	print(ord(flag[i]) ^ output[i], end = ' ')print("\n")
g = f(x)print("xor output with g")for n in output:	print(chr(n ^ next(g)), end = '')print("\n")
```
Alternative two-line solution:```Pythonexec('def f(x):'+'yield((x:=-~x)*x+-~-x)%727;'*100)g=f(470);print(*map(lambda c:chr(int(c)^next(g)),open('output.txt').read().split(' ')))```