Predict Choice / Limited Rand
the challenge I give is to win 50 times in a row with the client recorder against this RockPaperScissor-PythonServer
import SocketServer,threading,os,string
import random, time
f = open('secret.txt')
offset = int(f.readline().strip())
choices = {
'r': 'rock',
'p': 'paper',
's': 'scissors'
}
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
pass
class MyTCPHandler(SocketServer.BaseRequestHandler):
def handle(self):
rnd = random.Random()
# Initialize the random number generator to some secret value
# Note: the value of offset is too big to guess/bruteforce you need to find a better way :)
rnd.seed(int(time.time() + offset))
self.request.sendall("Rock paper scissors is back\n")
win_count = 0
play_again = True
while play_again:
while win_count < 50:
self.request.sendall("choose one [r] rock, [p] paper, [s] scissors: ")
your_choice = self.request.recv(1024).strip()
if not your_choice in 'rps':
continue
self.request.sendall("Your choice %s\n" % choices.get(your_choice))
my_choice = rnd.choice("rps")
self.request.sendall("My choice %s\n" % choices.get(my_choice))
if my_choice == your_choice:
self.request.sendall("Its a tie, sorry you need to win 50 times in a row, a tie is simply not good enough.\nWho ever said life was fair?\n")
break
if ((my_choice == 'r' and your_choice == 'p') or
(my_choice == 'p' and your_choice == 's') or
(my_choice == 's' and your_choice == 'r')):
win_count += 1
self.request.sendall("Arghhh. you beat me %s times\n" % win_count)
else:
self.request.sendall("You loose!\n")
break
if win_count == 50:
self.request.sendall("50 times in a row?!? are you some kind of mind reader?\n")
return
else:
win_count = 0
answer = ''
while answer not in ('y','n'):
self.request.sendall("Play again? (y/n): ")
answer = self.request.recv(1024).strip().lower()
if answer == 'n':
return
SocketServer.TCPServer.allow_reuse_address = True
server = ThreadedTCPServer(("0.0.0.0", 1178), MyTCPHandler)
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = True
server_thread.start()
server.serve_forever()
I read in the python random.py doc and on various sites that the kernel random number generator that uses the random pythons class (MersenneTwister) is not good for security-related stuff because it is predictable when an attacker manages to get 624 sequential numbers.
I already have a client playing 624 times of rock and in each round it detects a server selection, converts it to the corresponding array index in [rps], and writes that number to a file. So in the end a long file contains many 0s, 1s and 2s like this
0 1 0 2 2 0 ....
The most important line in the server code for me seems to be
my_choice = rnd.choice("rps")
which is implemented as (extract from random.py):
def choice(self, seq):
"""Choose a random element from a non-empty sequence."""
return seq[int(self.random() * len(seq))] # raises IndexError if seq is empty
There I read that for the prediction of the following numbers I need to record 624 consecutive numbers and restore the state by the change / cancellation of certain changes, but I believe that this requires a direct conclusion rng core, which is a float between [0.0, 1.0] ...
To get the main rng output from the sequence index, it seems like I just need to exactly flip the above "choice ()" function code, which would be something like
seq_value = seq[int(core_rng_out * len(seq))]
seq_index = int(core_rng_out * len(seq))
int^-1(seq_index) = core_rng_out * len(seq)
int^-1(seq_index) / len(seq) = core_rng_out
core_rng_out = int^-1(seq_index) / 3
The above should be something like solving a math equation for a specific variable. Divided by 3 because sequence of 3 sizes ("rps") but what is the inverse of pythons int (...)?!? Above, I tried to abstractly mark it as inverse by making it ^ -1.
And besides, it is even possible to get rng float at all?!?, Because in pythons int-doc it says when int (...) is assigned to float some truncation will / might happen ...?!
Or could it be completely wrong approach and I can beat the server in an easier way?
source to share
It seems to me that you can trick the server by initiating two connections at the same time (within the same second).
If they are initiated within the same second, the random seed will be the same (because of this line: rnd.seed(int(time.time() + offset))
This way the server will generate the same options for the two clients.
So what needs to be done is simply ask one client to go for any (possibly losing) strategy by recording the first 50 computers first. Then, knowing them, you can play on a different connection with exactly winning moves.
source to share