def order_geq_lex(c1: int, c2: int): pairs_c1, pairs_c2 = pairs(c1), pairs(c2) assert len(pairs_c1) == len(pairs_c2) constraints = smt.TRUE() for j in range(len(pairs_c1)): condition = smt.TRUE() for i in range(j): condition &= order_equal(pairs_c1[i], pairs_c2[i]) constraints &= smt.Implies(condition, order_geq(pairs_c1[j], pairs_c2[j])) return constraints
def query_oracle(self, dis_formula): self.solver_oracle.reset_assertions() c0 = self.attack_formulas.oracle_ckt_at_frame(0) for i in range(len(c0)): self.solver_oracle.add_assertion(c0[i]) dis_out = [] for d in range(1, self.unroll_depth + 1): c0 = self.attack_formulas.oracle_ckt_at_frame(d) for i in range(len(c0)): self.solver_oracle.add_assertion(c0[i]) self.solver_oracle.add_assertion(pystm.And(dis_formula[d - 1])) if not self.solver_oracle.is_sat(pystm.TRUE()): logging.critical('something is wrong in oracle query') exit() else: dip_out = [] # for w in self.oracle_cir.output_wires: for w in self.obf_cir.output_wires: f = pystm.Symbol(w + '@{}'.format(d)) dip_out.append(self.solver_oracle.get_value(f)) dis_out.append(dip_out) logging.info(dis_out) return dis_out
def import_xadd_mspn(filename): # type: (str) -> Density name = os.path.basename(filename) parts = name.split("_") real_vars = int(parts[1]) bool_vars = int(parts[2]) domain = Domain.make(["A_{}".format(i) for i in range(bool_vars)], {"x_{}".format(i): [0, 1] for i in range(real_vars)}) support = smt.TRUE() with open(filename) as f: weight = nested_to_smt(f.readlines()[0]) queries = [smt.TRUE()] return Density(domain, support, weight, queries)
def weight(self, a): var = self.literals.inv_numbered[abs(a)] abstraction = self.literals[var] if isinstance(abstraction, str): return [(smt.TRUE(), {abstraction})] else: if a < 0: abstraction = ~abstraction return [(abstraction, set())]
def make_distinct_bounds(domain): base_lower_bound = 0.13 base_upper_bound = 0.89 step = 0.01 variables = domain.get_symbols(domain.real_vars) bounds = smt.TRUE() for i in range(len(variables)): bounds &= variables[i] >= base_lower_bound + i * step # - i * step bounds &= variables[i] <= base_upper_bound - i * step # + i * step return bounds
def import_smt_synthetic(filename): # type: (str) -> Density with open(filename) as f: flat = json.load(f) domain = Domain.from_state(flat["synthetic_problem"]["problem"]["domain"]) queries = [smt.TRUE()] support = nested_to_smt(flat["synthetic_problem"]["problem"]["theory"]) & domain.get_bounds() weights = smt.Real(1) return Density(domain, support, weights, queries)
def query_dip_generator(self): dis_boolean = [] for d in range(1, self.unroll_depth + 1): dip_boolean = [] for w in self.obf_cir.input_wires: f = pystm.Symbol(w + '@{}'.format(d)) if self.solver_obf.get_py_value(f): dip_boolean.append(pystm.TRUE()) else: dip_boolean.append(pystm.FALSE()) dis_boolean.append(dip_boolean) return dis_boolean
def find_iter(data, domain, idx, finder, *finder_args): strat = RandomViolationsStrategy(10) excluded = [i for i in range(len(data)) if i not in idx] active_indices = random.sample(idx, min(20, len(idx))) excluded += active_indices result = smt.TRUE() #will be replaced while len(active_indices) > 0 and result is not None: result = finder(data, domain, active_indices, *finder_args) if result is None: break active_indices = list( strat.select_active(domain, data, result, excluded)) excluded += active_indices return result
def import_wmi_mspn(filename): # type: (str) -> Density q_file, s_file, w_file = ("{}.{}".format(filename, ext) for ext in ["query", "support", "weight"]) queries = [smt.TRUE()] if not os.path.exists(q_file) else [smt.read_smtlib(q_file)] support = smt.read_smtlib(s_file) if os.path.exists(w_file): weights = smt.read_smtlib(w_file) else: weights = smt.Real(1) name = os.path.basename(filename) parts = name.split("_") real_vars = int(parts[1]) bool_vars = int(parts[2]) domain = Domain.make(["A_{}".format(i) for i in range(bool_vars)], {"x_{}".format(i): [0, 1] for i in range(real_vars)}) return Density(domain, support, weights, queries)
def mutual_exclusive(n): domain = make_domain(n) symbols = domain.get_symbols(domain.real_vars) x, symbols = symbols[0], symbols[1:] bounds = make_distinct_bounds(domain) terms = [x <= v for v in symbols] disjunction = smt.TRUE() for i in range(n): for j in range(i + 1, n): disjunction &= ~terms[i] | ~terms[j] disjunction = smt.simplify(disjunction) & smt.Or(*terms) flipped_domain = Domain(list(reversed([v for v in domain.variables if v != "x"])) + ["x"], domain.var_types, domain.var_domains) return FileDensity(flipped_domain, disjunction & bounds, smt.Real(1.0))
def test_rejection_iff_bool(): domain = Domain.make(["a", "b"]) a, b = domain.get_symbols() print(domain) vol_t = RejectionEngine(domain, smt.TRUE(), smt.Real(1.0), 100000).compute_volume() vol1 = RejectionEngine(domain, (a | b) & (~a | ~b), smt.Real(1.0), 100000).compute_volume() vol2 = RejectionEngine(domain, smt.Iff(a, ~b), smt.Real(1.0), 100000).compute_volume() vol3 = RejectionEngine(domain, ~smt.Iff(a, b), smt.Real(1.0), 100000).compute_volume() print(vol1, vol2, vol3, vol_t) # print(PredicateAbstractionEngine(domain, a | b, smt.Real(1.0)).compute_volume()) print(XaddEngine(domain, a | b, smt.Real(1.0)).compute_volume()) quit()
def integrate(self, domain, convex_bounds: List[LinearInequality], polynomial: Polynomial): # TODO Use power of linear forms? b_geq_a = [] formula = smt.TRUE() for bound in convex_bounds: integer_bound = bound.scale_to_integer() formula &= integer_bound.to_smt() b_geq_a.append([integer_bound.b()] + [-integer_bound.a(v) for v in domain.real_vars]) monomials = [(Fraction(value).limit_denominator(), self.key_to_exponents(domain, key)) for key, value in polynomial.poly_dict.items()] with TemporaryFile(suffix=".hrep.latte") as bounds_file: with TemporaryFile(suffix=".poly.latte") as poly_file: with open(bounds_file, "w") as bounds_ref: print("{} {}".format(len(b_geq_a), len(domain.real_vars) + 1), file=bounds_ref) print(*[" ".join(map(str, e)) for e in b_geq_a], sep="\n", file=bounds_ref) with open(poly_file, "w") as poly_ref: print("[{}]".format(",".join("[{},[{}]]".format(m[0], ",".join(map(str, m[1]))) for m in monomials)), file=poly_ref) command = "integrate --valuation=integrate {} --monomials={} {}"\ .format(self.algorithm, poly_file, bounds_file) try: output = check_output(command, shell=True, stderr=DEVNULL).decode() except CalledProcessError: with smt.Solver() as solver: solver.add_assertion(formula) solver.solve() try: solver.get_model() except InternalSolverError: return 0.0 raise match = re.search(self.pattern, output) if not match: return 0.0 return float(Fraction(int(match.group(1)), int(match.group(2))))
def accuracy_approx(experiment): key = "accuracy_approx:{}".format(experiment.imported_from_file) if Properties.db.exists(key): return Properties.db.get(key) else: pysmt.environment.push_env() pysmt.environment.get_env().enable_infix_notation = True if os.path.basename(experiment.imported_from_file).startswith("synthetic"): db = Properties.get_db_synthetic(experiment) name = Properties.to_synthetic_name(experiment.imported_from_file) entry = db.get(name) domain = import_domain(json.loads(entry["domain"])) true_formula = nested_to_smt(entry["formula"]) else: density = Density.import_from(experiment.parameters.original_values["domain"]) domain = Domain(density.domain.variables, density.domain.var_types, Properties.get_bound(experiment)) true_formula = density.support learned_formula = nested_to_smt(experiment.results.formula) engine = RejectionEngine(domain, smt.TRUE(), smt.Real(1.0), 100000) accuracy = engine.compute_probability(smt.Iff(true_formula, learned_formula)) pysmt.environment.pop_env() print(accuracy) Properties.db.set(key, accuracy) return accuracy
def learn_partial(self, solver, domain, data, new_active_indices): # Constants n_b_original = len(domain.bool_vars) n_b = n_b_original * 2 n_r = len(domain.real_vars) n_h_original = self.half_space_count if n_r > 0 else 0 n_h = n_h_original * 2 if self.allow_negations else n_h_original n_c = self.conjunction_count n_d = len(data) real_features = [[row[v] for v in domain.real_vars] for row, _ in data] bool_features = [[row[v] for v in domain.bool_vars] for row, _ in data] labels = [row[1] for row in data] # Variables a_hr = [[ smt.Symbol("a_hr[{}][{}]".format(h, r), REAL) for r in range(n_r) ] for h in range(n_h_original)] b_h = [ smt.Symbol("b_h[{}]".format(h), REAL) for h in range(n_h_original) ] s_ch = [[smt.Symbol("s_ch[{}][{}]".format(c, h)) for h in range(n_h)] for c in range(n_c)] s_cb = [[smt.Symbol("s_cb[{}][{}]".format(c, b)) for b in range(n_b)] for c in range(n_c)] # Aux variables s_ih = [[smt.Symbol("s_ih[{}][{}]".format(i, h)) for h in range(n_h)] for i in range(n_d)] s_ic = [[smt.Symbol("s_ic[{}][{}]".format(i, c)) for c in range(n_c)] for i in range(n_d)] # Constraints for i in new_active_indices: x_r, x_b, label = real_features[i], bool_features[i], labels[i] for h in range(n_h_original): sum_coefficients = smt.Plus( [a_hr[h][r] * smt.Real(x_r[r]) for r in range(n_r)]) solver.add_assertion( smt.Iff(s_ih[i][h], sum_coefficients <= b_h[h])) for h in range(n_h_original, n_h): solver.add_assertion( smt.Iff(s_ih[i][h], ~s_ih[i][h - n_h_original])) for c in range(n_c): solver.add_assertion( smt.Iff( s_ic[i][c], smt.And([smt.TRUE()] + [(~s_ch[c][h] | s_ih[i][h]) for h in range(n_h)] + [ ~s_cb[c][b] for b in range(n_b_original) if not x_b[b] ] + [ ~s_cb[c][b] for b in range(n_b_original, n_b) if x_b[b - n_b_original] ]))) if label: solver.add_assertion(smt.Or([s_ic[i][c] for c in range(n_c)])) else: solver.add_assertion(smt.And([~s_ic[i][c] for c in range(n_c)])) solver.solve() model = solver.get_model() x_vars = [domain.get_symbol(domain.real_vars[r]) for r in range(n_r)] half_spaces = [ smt.Plus( [model.get_value(a_hr[h][r]) * x_vars[r] for r in range(n_r)]) <= model.get_value(b_h[h]) for h in range(n_h_original) ] + [ smt.Plus( [model.get_value(a_hr[h][r]) * x_vars[r] for r in range(n_r)]) > model.get_value(b_h[h]) for h in range(n_h - n_h_original) ] b_vars = [ domain.get_symbol(domain.bool_vars[b]) for b in range(n_b_original) ] bool_literals = [b_vars[b] for b in range(n_b_original)] bool_literals += [~b_vars[b] for b in range(n_b - n_b_original)] conjunctions = [[ half_spaces[h] for h in range(n_h) if model.get_py_value(s_ch[c][h]) ] + [ bool_literals[b] for b in range(n_b) if model.get_py_value(s_cb[c][b]) ] for c in range(n_c)] return smt.Or([smt.And(conjunction) for conjunction in conjunctions])
def find_clause(data, domain, active_indices, solver, n_h): # Constants n_b_original = len(domain.bool_vars) n_b = n_b_original * 2 n_r = len(domain.real_vars) n_d = len(data) real_features = [[row[v] for v in domain.real_vars] for row, _ in data] bool_features = [[row[v] for v in domain.bool_vars] for row, _ in data] labels = [row[1] for row in data] # Variables a_hr = [[ smt.Symbol("a_hr[{}][{}]".format(h, r), REAL) for r in range(n_r) ] for h in range(n_h)] b_h = [smt.Symbol("b_h[{}]".format(h), REAL) for h in range(n_h)] s_b = [smt.Symbol("s_b[{}]".format(b)) for b in range(n_b)] # Aux variables s_ih = [[smt.Symbol("s_ih[{}][{}]".format(i, h)) for h in range(n_h)] for i in range(n_d)] # Constraints for i in active_indices: x_r, x_b, label = real_features[i], bool_features[i], labels[i] for h in range(n_h): sum_coefficients = smt.Plus( [a_hr[h][r] * smt.Real(x_r[r]) for r in range(n_r)]) if label: solver.add_assertion( smt.Iff(s_ih[i][h], sum_coefficients + DELTA <= b_h[h])) else: solver.add_assertion( smt.Iff(s_ih[i][h], sum_coefficients - DELTA <= b_h[h])) if label: solver.add_assertion( smt.Or([smt.FALSE()] + [s_ih[i][h] for h in range(n_h)] + [s_b[b] for b in range(n_b_original) if x_b[b]] + [ s_b[b] for b in range(n_b_original, n_b) if not x_b[b - n_b_original] ])) else: solver.add_assertion( smt.And([smt.TRUE()] + [~s_ih[i][h] for h in range(n_h)] + [~s_b[b] for b in range(n_b_original) if x_b[b]] + [ ~s_b[b] for b in range(n_b_original, n_b) if not x_b[b - n_b_original] ])) if not solver.solve(): return None model = solver.get_model() x_vars = [domain.get_symbol(domain.real_vars[r]) for r in range(n_r)] half_spaces = [ smt.Plus([model.get_value(a_hr[h][r]) * x_vars[r] for r in range(n_r)]) <= model.get_value(b_h[h]) for h in range(n_h) ] b_vars = [ domain.get_symbol(domain.bool_vars[b]) for b in range(n_b_original) ] bool_literals = [b_vars[b] for b in range(n_b_original)] bool_literals += [~b_vars[b] for b in range(n_b - n_b_original)] return smt.Or( [half_spaces[h] for h in range(n_h)] + [bool_literals[b] for b in range(n_b) if model.get_py_value(s_b[b])])
def __init__(self, domain, support, weight, queries=None): self.domain = domain self.support = support self.weight = weight self.queries = queries if queries is not None else [smt.TRUE()]
def run_internal(self): density = Density.from_file(self["filename"]) solver = get_engine(self["solver"], density.domain, density.support, density.weight) if not density.queries or (len(density.queries) == 1 and density.queries[0] == smt.TRUE()): self["volumes"] = [solver.compute_volume()] else: self["volumes"] = [solver.compute_probabilities(density.queries)]
def perform(self): # process inputs if '.bench' in self.args.b: self.obf_cir = bench2circuit(self.args.o) self.oracle_cir = bench2circuit(self.args.b) else: logging.critical('verilog input is disabled! use main_formal') exit() # self.oracle_ast = ASTWrapper(parse_verilog(self.args.b), self.args.b) # self.obf_ast = ASTWrapper(parse_verilog(self.args.o), self.args.o) # # self.oracle_cir = self.oracle_ast.get_circuit(check_correctness=False, correct_order=False) # self.obf_cir = self.obf_ast.get_circuit(check_correctness=False, correct_order=False) self.oracle_cir.create_ce_circuit() self.obf_cir.create_ce_circuit() sort_circuits(self.oracle_cir, self.obf_cir) # perform attack self.solver_obf = pystm.Solver(name=self.solver_name) self.solver_key = pystm.Solver(name=self.solver_name) self.solver_oracle = pystm.Solver(name=self.solver_name) logging.warning('initial value for boundary={}, step={}, stop={}'.format(self.boundary, self.step, self.stop)) logging.warning('solver={}'.format(self.solver_name)) self.attack_formulas = FormulaGenerator(self.oracle_cir, self.obf_cir) # add k0 != k1 self.solver_obf.add_assertion(self.attack_formulas.key_inequality_ckt) # assumptions for inequality of dip generator outputs assumptions = self.attack_formulas.dip_gen_assumption(1) # get initial states and the first copy of the circuit for i in range(2): c0, c1 = self.attack_formulas.obf_ckt_at_frame(i) for j in range(len(c0)): self.solver_obf.add_assertion(c0[j]) self.solver_obf.add_assertion(c1[j]) while 1: # query dip generator if self.solver_obf.is_sat(assumptions): dis_boolean = self.query_dip_generator() dis_formula = [] for i in range(1, len(dis_boolean) + 1): dis_formula.append(get_formulas(self.obf_cir.input_wires, dis_boolean[i-1], '@{}'.format(i))) logging.info(dis_formula) dis_out = self.query_oracle(dis_formula) self.add_dip_checker(dis_boolean, dis_out) self.iteration += 1 logging.warning('iteration={}, depth={}'.format(self.iteration, self.unroll_depth)) self.highest_depth = self.unroll_depth else: if (self.solver_obf.is_sat(pystm.TRUE()) or self.iteration == 0) and self.unroll_depth < self.stop: # two agreeing keys are found, but no dip can be found # also it should keep unrolling the circuit until at least one dip is found # then it can decide on uc success if self.unroll_depth == self.boundary: logging.warning('uc failed') # check ce if self.ce_check(): return True elif self.umc_check(): continue else: # increase boundary # self.unroll_depth += 1 self.boundary += self.step else: # increase unroll depth self.unroll_depth += 1 assumptions = self.attack_formulas.dip_gen_assumption(self.unroll_depth) c0, c1 = self.attack_formulas.obf_ckt_at_frame(self.unroll_depth) for i in range(len(c0)): self.solver_obf.add_assertion(c0[i]) self.solver_obf.add_assertion(c1[i]) logging.warning('increasing unroll depth to {}'.format(self.unroll_depth)) elif self.unroll_depth >= self.stop: logging.warning('stopped at {}'.format(self.stop)) self.print_keys() return True else: # key is unique logging.warning('uc successful') self.print_keys() return True
def times_neutral(self): return [(smt.TRUE(), set())]