mirror of
https://github.com/TACIXAT/XorShift128Plus
synced 2025-01-17 20:53:00 +00:00
Fix Chrome.
This commit is contained in:
parent
a9c9d6a786
commit
f2ae4a3f9e
6
README
6
README
@ -1,10 +1,14 @@
|
|||||||
For usage on the LA Time's powerball simulator. Careful about clicking the page.
|
For usage on the LA Time's powerball simulator. Careful about clicking the page.
|
||||||
|
|
||||||
|
Blog post: https://blog.securityevaluators.com/hacking-the-javascript-lottery-80cc437e3b7f
|
||||||
|
|
||||||
Run the following snippet in your browser's console.
|
Run the following snippet in your browser's console.
|
||||||
|
|
||||||
_ = []; for(var i=0; i<5; ++i) { _.push(Math.random()) } ; console.log(_)
|
_ = []; for(var i=0; i<5; ++i) { _.push(Math.random()) } ; console.log(_)
|
||||||
|
|
||||||
Paste at least 3 of those values into the dubs array in main().
|
Paste at least 3 of those (5 for Chrome) values into the dubs array in main().
|
||||||
|
|
||||||
|
It will warn you if the model is too "loose" and has multiple solutions.
|
||||||
|
|
||||||
Set the browser in main() to Chrome or Firefox. (Safari hasn't updated yet.)
|
Set the browser in main() to Chrome or Firefox. (Safari hasn't updated yet.)
|
||||||
|
|
||||||
|
95
xs128p.py
95
xs128p.py
@ -2,20 +2,24 @@ import sys
|
|||||||
import math
|
import math
|
||||||
import struct
|
import struct
|
||||||
import random
|
import random
|
||||||
sys.path.append('/home/dgoddard/tools/z3/build')
|
|
||||||
from z3 import *
|
from z3 import *
|
||||||
|
|
||||||
|
MASK = 0xFFFFFFFFFFFFFFFF
|
||||||
|
|
||||||
# xor_shift_128_plus algorithm
|
# xor_shift_128_plus algorithm
|
||||||
def xs128p(state0, state1):
|
def xs128p(state0, state1, browser):
|
||||||
s1 = state0 & 0xFFFFFFFFFFFFFFFF
|
s1 = state0 & MASK
|
||||||
s0 = state1 & 0xFFFFFFFFFFFFFFFF
|
s0 = state1 & MASK
|
||||||
s1 ^= (s1 << 23) & 0xFFFFFFFFFFFFFFFF
|
s1 ^= (s1 << 23) & MASK
|
||||||
s1 ^= (s1 >> 17) & 0xFFFFFFFFFFFFFFFF
|
s1 ^= (s1 >> 17) & MASK
|
||||||
s1 ^= s0 & 0xFFFFFFFFFFFFFFFF
|
s1 ^= s0 & MASK
|
||||||
s1 ^= (s0 >> 26) & 0xFFFFFFFFFFFFFFFF
|
s1 ^= (s0 >> 26) & MASK
|
||||||
state0 = state1 & 0xFFFFFFFFFFFFFFFF
|
state0 = state1 & MASK
|
||||||
state1 = s1 & 0xFFFFFFFFFFFFFFFF
|
state1 = s1 & MASK
|
||||||
generated = (state0 + state1) & 0xFFFFFFFFFFFFFFFF
|
if browser == 'chrome':
|
||||||
|
generated = state0 & MASK
|
||||||
|
else:
|
||||||
|
generated = (state0 + state1) & MASK
|
||||||
|
|
||||||
return state0, state1, generated
|
return state0, state1, generated
|
||||||
|
|
||||||
@ -29,11 +33,14 @@ def sym_xs128p(slvr, sym_state0, sym_state1, generated, browser):
|
|||||||
s1 ^= LShR(s0, 26)
|
s1 ^= LShR(s0, 26)
|
||||||
sym_state0 = sym_state1
|
sym_state0 = sym_state1
|
||||||
sym_state1 = s1
|
sym_state1 = s1
|
||||||
calc = (sym_state0 + sym_state1)
|
if browser == 'chrome':
|
||||||
|
calc = sym_state0
|
||||||
|
else:
|
||||||
|
calc = (sym_state0 + sym_state1)
|
||||||
|
|
||||||
condition = Bool('c%d' % int(generated * random.random()))
|
condition = Bool('c%d' % int(generated * random.random()))
|
||||||
if browser == 'chrome':
|
if browser == 'chrome':
|
||||||
impl = Implies(condition, (calc & 0xFFFFFFFFFFFFF) == int(generated))
|
impl = Implies(condition, LShR(calc, 12) == int(generated))
|
||||||
elif browser == 'firefox' or browser == 'safari':
|
elif browser == 'firefox' or browser == 'safari':
|
||||||
# Firefox and Safari save an extra bit
|
# Firefox and Safari save an extra bit
|
||||||
impl = Implies(condition, (calc & 0x1FFFFFFFFFFFFF) == int(generated))
|
impl = Implies(condition, (calc & 0x1FFFFFFFFFFFFF) == int(generated))
|
||||||
@ -45,15 +52,20 @@ def reverse17(val):
|
|||||||
return val ^ (val >> 17) ^ (val >> 34) ^ (val >> 51)
|
return val ^ (val >> 17) ^ (val >> 34) ^ (val >> 51)
|
||||||
|
|
||||||
def reverse23(val):
|
def reverse23(val):
|
||||||
return (val ^ (val << 23) ^ (val << 46)) & 0xFFFFFFFFFFFFFFFF
|
return (val ^ (val << 23) ^ (val << 46)) & MASK
|
||||||
|
|
||||||
def xs128p_backward(state0, state1):
|
def xs128p_backward(state0, state1, browser):
|
||||||
prev_state1 = state0
|
prev_state1 = state0
|
||||||
prev_state0 = state1 ^ (state0 >> 26)
|
prev_state0 = state1 ^ (state0 >> 26)
|
||||||
prev_state0 = prev_state0 ^ state0
|
prev_state0 = prev_state0 ^ state0
|
||||||
prev_state0 = reverse17(prev_state0)
|
prev_state0 = reverse17(prev_state0)
|
||||||
prev_state0 = reverse23(prev_state0)
|
prev_state0 = reverse23(prev_state0)
|
||||||
generated = (prev_state0 + prev_state1) & 0xFFFFFFFFFFFFFFFF
|
# this is only called from an if chrome
|
||||||
|
# but let's be safe in case someone copies it out
|
||||||
|
if browser == 'chrome':
|
||||||
|
generated = prev_state0
|
||||||
|
else:
|
||||||
|
generated = (prev_state0 + prev_state1) & MASK
|
||||||
return prev_state0, prev_state1, generated
|
return prev_state0, prev_state1, generated
|
||||||
|
|
||||||
# Print 'last seen' random number
|
# Print 'last seen' random number
|
||||||
@ -66,9 +78,9 @@ def xs128p_backward(state0, state1):
|
|||||||
# with an arrow.
|
# with an arrow.
|
||||||
def power_ball(generated, browser):
|
def power_ball(generated, browser):
|
||||||
# for each random number (skip 4 of 5 that we generated)
|
# for each random number (skip 4 of 5 that we generated)
|
||||||
for idx in xrange(len(generated[4:])):
|
for idx in range(len(generated[4:])):
|
||||||
# powerball range is 1 to 69
|
# powerball range is 1 to 69
|
||||||
poss = range(1, 70)
|
poss = list(range(1, 70))
|
||||||
# base index 4 to skip
|
# base index 4 to skip
|
||||||
gen = generated[4+idx:]
|
gen = generated[4+idx:]
|
||||||
# get 'last seen' number
|
# get 'last seen' number
|
||||||
@ -77,11 +89,11 @@ def power_ball(generated, browser):
|
|||||||
# make sure we have enough numbers
|
# make sure we have enough numbers
|
||||||
if len(gen) < 6:
|
if len(gen) < 6:
|
||||||
break
|
break
|
||||||
print g0
|
print(g0)
|
||||||
|
|
||||||
# generate 5 winning numbers
|
# generate 5 winning numbers
|
||||||
nums = []
|
nums = []
|
||||||
for jdx in xrange(5):
|
for jdx in range(5):
|
||||||
index = int(gen[jdx] * len(poss))
|
index = int(gen[jdx] * len(poss))
|
||||||
val = poss[index]
|
val = poss[index]
|
||||||
poss = poss[:index] + poss[index+1:]
|
poss = poss[:index] + poss[index+1:]
|
||||||
@ -89,28 +101,28 @@ def power_ball(generated, browser):
|
|||||||
|
|
||||||
# print indicator
|
# print indicator
|
||||||
if idx == 0 and browser == 'chrome':
|
if idx == 0 and browser == 'chrome':
|
||||||
print '--->',
|
print('--->', end='')
|
||||||
elif idx == 2 and browser == 'firefox':
|
elif idx == 2 and browser == 'firefox':
|
||||||
print '--->',
|
print('--->', end='')
|
||||||
else:
|
else:
|
||||||
print ' ',
|
print(' ', end='')
|
||||||
# print winning numbers
|
# print winning numbers
|
||||||
print sorted(nums),
|
print(sorted(nums), end='')
|
||||||
|
|
||||||
# generate / print power number or w/e it's called
|
# generate / print power number or w/e it's called
|
||||||
double = gen[5]
|
double = gen[5]
|
||||||
val = int(math.floor(double * 26) + 1)
|
val = int(math.floor(double * 26) + 1)
|
||||||
print val
|
print(val)
|
||||||
|
|
||||||
# Firefox nextDouble():
|
# Firefox nextDouble():
|
||||||
# (rand_uint64 & ((1 << 53) - 1)) / (1 << 53)
|
# (rand_uint64 & ((1 << 53) - 1)) / (1 << 53)
|
||||||
# Chrome nextDouble():
|
# Chrome nextDouble():
|
||||||
# ((rand_uint64 & ((1 << 52) - 1)) | 0x3FF0000000000000) - 1.0
|
# (state0 | 0x3FF0000000000000) - 1.0
|
||||||
# Safari weakRandom.get():
|
# Safari weakRandom.get():
|
||||||
# (rand_uint64 & ((1 << 53) - 1) * (1.0 / (1 << 53)))
|
# (rand_uint64 & ((1 << 53) - 1) * (1.0 / (1 << 53)))
|
||||||
def to_double(browser, out):
|
def to_double(browser, out):
|
||||||
if browser == 'chrome':
|
if browser == 'chrome':
|
||||||
double_bits = (out & 0xFFFFFFFFFFFFF) | 0x3FF0000000000000
|
double_bits = (out >> 12) | 0x3FF0000000000000
|
||||||
double = struct.unpack('d', struct.pack('<Q', double_bits))[0] - 1
|
double = struct.unpack('d', struct.pack('<Q', double_bits))[0] - 1
|
||||||
elif browser == 'firefox':
|
elif browser == 'firefox':
|
||||||
double = float(out & 0x1FFFFFFFFFFFFF) / (0x1 << 53)
|
double = float(out & 0x1FFFFFFFFFFFFF) / (0x1 << 53)
|
||||||
@ -126,22 +138,25 @@ def main():
|
|||||||
# browser = 'safari'
|
# browser = 'safari'
|
||||||
browser = 'chrome'
|
browser = 'chrome'
|
||||||
# browser = 'firefox'
|
# browser = 'firefox'
|
||||||
print 'BROWSER: %s' % browser
|
print('BROWSER: %s' % browser)
|
||||||
|
|
||||||
# In your browser's JavaScript console:
|
# In your browser's JavaScript console:
|
||||||
# _ = []; for(var i=0; i<5; ++i) { _.push(Math.random()) } ; console.log(_)
|
# _ = []; for(var i=0; i<5; ++i) { _.push(Math.random()) } ; console.log(_)
|
||||||
# Enter at least the 3 first random numbers you observed here:
|
# Enter at least the 3 first random numbers you observed here:
|
||||||
dubs = [0.4752549301773037, 0.08162196013326506, 0.8333085432653353]
|
# Observations show Chrome needs ~5
|
||||||
|
dubs = [
|
||||||
|
0.5368584449767335, 0.883588766746984, 0.7895949638905317,
|
||||||
|
0.5106241305628436, 0.49965622623126693]
|
||||||
if browser == 'chrome':
|
if browser == 'chrome':
|
||||||
dubs = dubs[::-1]
|
dubs = dubs[::-1]
|
||||||
|
|
||||||
print dubs
|
print(dubs)
|
||||||
|
|
||||||
# from the doubles, generate known piece of the original uint64
|
# from the doubles, generate known piece of the original uint64
|
||||||
generated = []
|
generated = []
|
||||||
for idx in xrange(3):
|
for idx in range(len(dubs)):
|
||||||
if browser == 'chrome':
|
if browser == 'chrome':
|
||||||
recovered = struct.unpack('<Q', struct.pack('d', dubs[idx] + 1))[0] & 0xFFFFFFFFFFFFF
|
recovered = struct.unpack('<Q', struct.pack('d', dubs[idx] + 1))[0] & (MASK >> 12)
|
||||||
elif browser == 'firefox':
|
elif browser == 'firefox':
|
||||||
recovered = dubs[idx] * (0x1 << 53)
|
recovered = dubs[idx] * (0x1 << 53)
|
||||||
elif browser == 'safari':
|
elif browser == 'safari':
|
||||||
@ -157,7 +172,7 @@ def main():
|
|||||||
|
|
||||||
# run symbolic xorshift128+ algorithm for three iterations
|
# run symbolic xorshift128+ algorithm for three iterations
|
||||||
# using the recovered numbers as constraints
|
# using the recovered numbers as constraints
|
||||||
for ea in xrange(3):
|
for ea in range(len(dubs)):
|
||||||
sym_state0, sym_state1, ret_conditions = sym_xs128p(slvr, sym_state0, sym_state1, generated[ea], browser)
|
sym_state0, sym_state1, ret_conditions = sym_xs128p(slvr, sym_state0, sym_state1, generated[ea], browser)
|
||||||
conditions += ret_conditions
|
conditions += ret_conditions
|
||||||
|
|
||||||
@ -166,21 +181,27 @@ def main():
|
|||||||
m = slvr.model()
|
m = slvr.model()
|
||||||
state0 = m[ostate0].as_long()
|
state0 = m[ostate0].as_long()
|
||||||
state1 = m[ostate1].as_long()
|
state1 = m[ostate1].as_long()
|
||||||
|
slvr.add(Or(ostate0 != m[ostate0], ostate1 != m[ostate1]))
|
||||||
|
if slvr.check(conditions) == sat:
|
||||||
|
print('WARNING: multiple solutions found! use more dubs!')
|
||||||
|
print('state', state0, state1)
|
||||||
|
|
||||||
generated = []
|
generated = []
|
||||||
# generate random numbers from recovered state
|
# generate random numbers from recovered state
|
||||||
for idx in xrange(15):
|
for idx in range(15):
|
||||||
if browser == 'chrome':
|
if browser == 'chrome':
|
||||||
state0, state1, out = xs128p_backward(state0, state1)
|
state0, state1, out = xs128p_backward(state0, state1, browser)
|
||||||
|
out = state0 & MASK
|
||||||
else:
|
else:
|
||||||
state0, state1, out = xs128p(state0, state1)
|
state0, state1, out = xs128p(state0, state1, browser)
|
||||||
|
|
||||||
double = to_double(browser, out)
|
double = to_double(browser, out)
|
||||||
|
print('gen', double)
|
||||||
generated.append(double)
|
generated.append(double)
|
||||||
|
|
||||||
# use generated numbers to predict powerball numbers
|
# use generated numbers to predict powerball numbers
|
||||||
power_ball(generated, browser)
|
power_ball(generated, browser)
|
||||||
else:
|
else:
|
||||||
print 'UNSAT'
|
print('UNSAT')
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
Loading…
Reference in New Issue
Block a user