UNPKG

js-randomness-predictor

Version:
88 lines (87 loc) 3.19 kB
import * as z3 from "z3-solver"; export default class ChromeRandomnessPredictor { #isInitialized = false; #mask = 0xffffffffffffffffn; #concreteState0; #concreteState1; #context; #solver; #seState0; #seState1; #s0Ref; #s1Ref; sequence = undefined; constructor(sequence) { this.sequence = sequence; } async #initialize() { if (this.#isInitialized) { return true; } try { const { Context } = await z3.init(); this.#context = Context("main"); this.#solver = new this.#context.Solver(); this.#seState0 = this.#context.BitVec.const("se_state0", 64); this.#seState1 = this.#context.BitVec.const("se_state1", 64); this.#s0Ref = this.#seState0; this.#s1Ref = this.#seState1; const reversedSequence = [...(this.sequence || [])].reverse(); for (const value of reversedSequence) { this.#xorShift128pSymbolic(); const mantissa = this.#recoverMantissa(value); const state0Shifted = this.#context.BitVec.val(mantissa, 64); this.#solver.add(this.#seState0.lshr(11).eq(state0Shifted)); } const result = await this.#solver.check(); if (result !== "sat") { return Promise.reject("Unsat"); } const model = this.#solver.model(); this.#concreteState0 = model.get(this.#s0Ref).value(); this.#concreteState1 = model.get(this.#s1Ref).value(); this.#isInitialized = true; return true; } catch (e) { return Promise.reject(e); } } async predictNext() { await this.#initialize(); if (this.#concreteState0 === undefined || this.#concreteState1 === undefined) { throw new Error(`[Chrome Predictor] Concrete states not defined! Something went wrong.`); } return this.#toDouble(this.#xorShift128pConcreteBackwards()); } #xorShift128pSymbolic() { if (!this.#seState0 || !this.#seState1) { throw new Error("[Chrome Predictor] Symbolic states not initialized"); } const se_s1 = this.#seState0; const se_s0 = this.#seState1; this.#seState0 = se_s0; let newS1 = se_s1.xor(se_s1.shl(23)); newS1 = newS1.xor(newS1.lshr(17)); newS1 = newS1.xor(se_s0); newS1 = newS1.xor(se_s0.lshr(26)); this.#seState1 = newS1; } #xorShift128pConcreteBackwards() { const result = this.#concreteState0; let ps1 = this.#concreteState0; let ps0 = this.#concreteState1 ^ (ps1 >> 26n); ps0 ^= ps1; ps0 = (ps0 ^ (ps0 >> 17n) ^ (ps0 >> 34n) ^ (ps0 >> 51n)) & this.#mask; ps0 = (ps0 ^ (ps0 << 23n) ^ (ps0 << 46n)) & this.#mask; this.#concreteState0 = ps0; this.#concreteState1 = ps1; return result; } #recoverMantissa(double) { return BigInt(Math.floor(double * Number(1n << 53n))); } #toDouble(val) { return Number(val >> 11n) / Math.pow(2, 53); } }