import sys import math import struct import random sys.path.append('/path/to/z3/build/') from z3 import * # xor_shift_128_plus algorithm def xs128p(state0, state1): s1 = state0 & 0xFFFFFFFFFFFFFFFF s0 = state1 & 0xFFFFFFFFFFFFFFFF s1 ^= (s1 << 23) & 0xFFFFFFFFFFFFFFFF s1 ^= (s1 >> 17) & 0xFFFFFFFFFFFFFFFF s1 ^= s0 & 0xFFFFFFFFFFFFFFFF s1 ^= (s0 >> 26) & 0xFFFFFFFFFFFFFFFF state0 = state1 & 0xFFFFFFFFFFFFFFFF state1 = s1 & 0xFFFFFFFFFFFFFFFF generated = (state0 + state1) & 0xFFFFFFFFFFFFFFFF return state0, state1, generated # Symbolic execution of xs128p def sym_xs128p(slvr, sym_state0, sym_state1, generated, browser): s1 = sym_state0 s0 = sym_state1 s1 ^= (s1 << 23) s1 ^= LShR(s1, 17) s1 ^= s0 s1 ^= LShR(s0, 26) sym_state0 = sym_state1 sym_state1 = s1 calc = (sym_state0 + sym_state1) condition = Bool('c%d' % int(generated * random.random())) if browser == 'chrome': impl = Implies(condition, (calc & 0xFFFFFFFFFFFFF) == int(generated)) elif browser == 'firefox' or browser == 'safari': # Firefox and Safari save an extra bit impl = Implies(condition, (calc & 0x1FFFFFFFFFFFFF) == int(generated)) slvr.add(impl) return sym_state0, sym_state1, [condition] # Print 'last seen' random number # and winning numbers following that. # This was for debugging. We know that Math.random() # is called in the browser twice for each page click # in Chrome and once for each page click in Firefox. # Since we have to click once to enter the numbers # and once for Play, we indicate the winning numbers # with an arrow. def power_ball(generated, browser): # for each random number (skip 4 of 5 that we generated) for idx in xrange(len(generated[4:])): # powerball range is 1 to 69 poss = range(1, 70) # base index 4 to skip gen = generated[4+idx:] # get 'last seen' number g0 = gen[0] gen = gen[1:] # make sure we have enough numbers if len(gen) < 6: break print g0 # generate 5 winning numbers nums = [] for jdx in xrange(5): index = int(gen[jdx] * len(poss)) val = poss[index] poss = poss[:index] + poss[index+1:] nums.append(val) # print indicator if idx == 4 and browser == 'chrome': print '--->', elif idx == 2 and browser == 'firefox': print '--->', else: print ' ', # print winning numbers print sorted(nums), # generate / print power number or w/e it's called double = gen[5] val = int(math.floor(double * 26) + 1) print val # Firefox nextDouble(): # (rand_uint64 & ((1 << 53) - 1)) / (1 << 53) # Chrome nextDouble(): # (rand_uint64 & ((1 << 52) - 1)) | 0x3FF0000000000000 # Safari weakRandom.get(): # (rand_uint64 & ((1 << 53) - 1) * (1.0 / (1 << 53))) def main(): # Note: # Safari tests have always turned up UNSAT # Wait for an update from Apple? # browser = 'safari' browser = 'chrome' # browser = 'firefox' print 'BROWSER: %s' % browser # In your browser's JavaScript console: # _ = []; for(var i=0; i<5; ++i) { _.push(Math.random()) } ; console.log(_) # Enter at least 3 random numbers you observed here: dubs = [ 0.8817331322829662, 0.31765120036119443, 0.3301985901101909 ] print dubs # from the doubles, generate known piece of the original uint64 generated = [] for idx in xrange(3): if browser == 'chrome': recovered = struct.unpack('