def __call__(self, iteration: int, nonces: List[Constant], P: List[Term], C: List[Term]): IV = nonces[0] i = iteration - 1 # Create substitution between symbolic plain and cipher texts # and the symbolic instantiations of them in MOOProgram sigma = SubstituteTerm() subterms = get_vars(self.term) for subterm in subterms: if subterm.symbol[0] == "P": if '-' not in subterm.symbol: # Assume we mean current plaintext sigma.add(subterm, P[-1]) else: j = int(subterm.symbol[4:-1]) if j > i: # If we request for a cipher block that doesn't exist yet # due to the current session length # then map the subterm to a different nounce #sigma.add(subterm, Constant(IV.symbol + f"_{j}")) sigma.add(subterm, Constant(IV.symbol)) else: sigma.add(subterm, P[-j]) elif subterm.symbol[0] == "C": j = int(subterm.symbol[4:-1]) if j > i: # If we request for a cipher block that doesn't exist yet # due to the current session length # then map the subterm to a different nounce #sigma.add(subterm, Constant(IV.symbol + f"_{j}")) sigma.add(subterm, Constant(IV.symbol)) else: sigma.add(subterm, C[-j]) return self.term * sigma
def build_a_substitution_from(self, v, eq): #Build a substitution from equation eq. The domain is variable v. #For example, if v: x, eq: f(y) + a = x + b, #then, the result would be: x |-> f(y) + a + b #Assume that there are no duplicate terms in eq. args = xor_to_list(eq.left_side) for arg in args: if (v == arg): args.remove(arg) range = list_to_xor(args) sigma = SubstituteTerm() sigma.add(v, range) return sigma
def combine_two_substitutions(subst1, subst2): #Combine two substitutions, and get a list of combined substitutions. #For example, if sub1 = {x1 |-> f(x), x2 |-> b}, sub2 = {x1 |-> f(a)}, #then the result would be [{x1 |-> f(a), x2 |-> b}] #print("combining") #print(sub1) #print("and") #print(sub2) #print("end") sub1 = deepcopy(subst1) sub2 = deepcopy(subst2) domain1 = list(sub1.domain()) domain2 = list(sub2.domain()) #if(sub2 == None): if (domain2 == []): return [sub1] else: #domain2 = list(sub2.domain()) first_var_in_sub2 = domain2[0] first_var_maps_to_in_sub2 = first_var_in_sub2 * sub2 if (first_var_in_sub2 in domain1): first_var_maps_to_in_sub1 = first_var_in_sub2 * sub1 if (first_var_maps_to_in_sub1 == first_var_maps_to_in_sub2): #print("duplicate variables map to the same term") sub2.remove(first_var_in_sub2) return combine_two_substitutions(sub1, sub2) else: #print("duplicate variables map to different terms") eq = Equation(convert_to_xorterm(first_var_maps_to_in_sub1), convert_to_xorterm(first_var_maps_to_in_sub2)) eqs = Equations([eq]) unifiers = xor_unification(eqs) results = [] sub2.remove(first_var_in_sub2) for unifier in unifiers: result = combine_two_substitutions(sub1 * unifier, sub2 * unifier) results = results + result return results else: #print("no duplicate variables found") sigma = SubstituteTerm() sigma.add(first_var_in_sub2, first_var_maps_to_in_sub2) sub1 = sub1 * sigma sub2.remove(first_var_in_sub2) return combine_two_substitutions(sub1, sub2)
def coalesce(U2: Set[Equation]): #Rule (a) Uremove = set() for e in list(U2): if isinstance(e.right_side, Variable) and isinstance( e.left_side, Variable) and e not in Uremove: sigma = SubstituteTerm() sigma.add(e.left_side, e.right_side) for e2 in list(U2): if e2 != e: if (isinstance(e2.left_side, FuncTerm) or isinstance(e2.left_side, Variable)): e2.left_side = e2.left_side * sigma if (isinstance(e2.right_side, FuncTerm) or isinstance(e2.right_side, Variable)): e2.right_side = e2.right_side * sigma #U2.remove(e) Uremove.add(e) return U2
def elim_tk(state): #Try applying the "elim_tk" rule #This rule applied if an equation is of the form: s_1 xor ... xor s_m = t_1 xor ... xor t_n, #where no s_i or t_j is d-rooted or e-rooted, and some s_i is a indexed variable Tk. #Tk = tk; id (applicable) #Tk xor e(Tk, C1) = tk xor e(tk, c1); id (not applicable) #If successful, returns "True" and the new state #Otherwise, returns "False" and the original state #Example: Tk = tk; id ==> true, ; {Tk |-> tk} #Example: Tk xor e(Tk, C1) = tk xor e(tk, c1); id ==> false, Tk xor e(Tk, C1) = tk xor e(tk, c1); id eqs = state.equations subst = state.substitution first = eqs[0] remaining_eqs = eqs[1:] lhs = first.left_side rhs = first.right_side lhs_summands = summands(lhs) rhs_summands = summands(rhs) applicable = (not containsDorE(lhs_summands)) and containsT(lhs_summands) new_subst = SubstituteTerm() remaining_terms = [] results = [] if (applicable): (var, remaining_terms) = pickT(lhs_summands) remaining_terms += rhs_summands if (len(remaining_terms) == 1): new_subst.add(var, remaining_terms[0]) else: new_subst.add(var, xor(*remaining_terms)) for t in remaining_eqs: results.append( Equation(t.left_side * new_subst, t.right_side * new_subst)) return (applicable, Unification_state(results, subst * new_subst))
def _changeVars(overlaping_vars: List[Variable], term: Term, hypothesis: Term, conclusion: Term): """Change variable names in hypothesis & conclusion to not overlap with overlapping_vars""" hypothesis = deepcopy(hypothesis) conclusion = deepcopy(conclusion) all_vars = get_vars(term, unique=True) | \ get_vars(hypothesis, unique=True) | \ get_vars(conclusion, unique=True) new_vars: List[Variable] = [] # Go through all the variables that share the same symbol between the term and rewrite rule # and change the variables in the rewrite rule for v in overlaping_vars: new_var = v # Keep renaming variable in rewrite rule until it is not an already existing variable while new_var in all_vars: new_var = Variable(new_var.symbol + "_1", new_var.sort) new_vars.append(new_var) # Create substitution between the old and new variable names and apply them s = SubstituteTerm() for old_v, new_v in zip(overlaping_vars, new_vars): s.add(old_v, new_v) return hypothesis * s, conclusion * s
def subformulaapply(formula: Formula, sigma: SubstituteTerm): """Applies a substitution to a formula.""" # Can't apply a substitution to a bool or Proposition if isinstance(formula, (bool, Proposition)): return formula if isinstance(formula, Predicate): # If the arity is zero, then it's a Proposition if formula.relation.arity == 0: return formula # Otherwise, apply the substitution to all # the terms in the predicate new_terms = [term * sigma for term in formula.args] return formula.relation(*new_terms) if isinstance(formula, Connective): return formula.__class__(subformulaapply(formula[0], sigma), subformulaapply(formula[1], sigma)) if isinstance(formula, Not): return Not(subformulaapply(formula.subformula, sigma)) if isinstance(formula, (ForAll, Exists)): # TODO: This is based on how Substitions are # currently handled. If we decide that its a bug # we'll have to rewrite this logic.... if formula.bound_variable in sigma.range(): new_bvar = _fresh_var_in_formula(formula) sigma.add(formula.bound_variable, new_bvar) return formula.__class__( new_bvar, subformulaapply(formula.subformula, sigma)) if formula.bound_Variable in sigma.domain(): sigma.remove(formula.bound_variable) return formula.__class__(formula.bound_variable, subformulaapply(new_subform, sigma)) raise ValueError(f"Unexpected type: {type(formula)}.")
non_zeros = Sort("non_zeros", reals) divide = Function("divide", 2, [reals, non_zeros], reals) one = Constant("1", non_zeros) zero = Constant("0", reals) try: divide(one, zero) except Exception: print("Cannot divide by zero. Check.") # Directed Acyclic Graphs # From page 16 # F(g(x, a), g(x,a)) dag = TermDAG(f(g(x, a), g(x, a))) dag.show() ## Substitutions sigma = SubstituteTerm() # Add the mapping x -> f(y) sigma.add(x, g(y, c)) print(f(x, b)) print("Substitutions: ", sigma) # Apply the substitution to the term f(x, b) print(f(x, b) * sigma) sigma2 = SubstituteTerm() sigma2.add(y, a) print("Another Substitution: ", sigma2) print( "Composing both substitutions and applying it to a term: f(x, b) * sigma * sigma2" ) print(f(x, b) * sigma * sigma2)