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_list_of_substitutions(lst): #It takes lst is a list of lists of substitutions, #returns a list of substitutions. #For example, if lst is: [[{x |-> f(z)}, {x |-> a}], [{x |-> f(b)}]], #then the result is [{x |-> f(b)}] sigma = SubstituteTerm() return combine_list_of_substitutions_helper(lst, [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 xor_unification(eqs): equations = purify_equations(eqs) diseqs = Disequations([]) subst = SubstituteTerm() state = XOR_proof_state(equations, diseqs, subst) solutions = xor_unification_helper(state) return solutions
def add_empty_subst_to_dictionary(dict): new_dict = {} for key in dict.keys(): old_value = dict[key] new_value = [] for val in old_value: new_value.append(ConstrainedTerm(val, SubstituteTerm())) new_dict.update({key: new_value}) return new_dict
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 solve(U2: Set[Equation]): #collect the \Sigma_0 terms f_terms = set() for e in U2: if isinstance(e.right_side, FuncTerm) and str( e.right_side.function) == "f": f_terms.add(e) delta = SubstituteTerm() #call ac-unif delta = ac_unify(f_terms) return (delta)
def instantiate_a_constraint(constraint, sigma): #Instantiate a constraint using sigma #For example, if constraint is {x1: [a, b], x2: [f(x1)]}, and sigma is {x1 |-> a} #then the result is {x1: [a, b], x2: [f(a)]} new_constraints = {} for key in constraint.keys(): old_constraint = constraint[key] new_constraint = [] for o in old_constraint: new_constraint.append( ConstrainedTerm(o.term * sigma, SubstituteTerm())) new_constraints.update({key: new_constraint}) return new_constraints
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 check_security(tag): if not check_syntax(tag): print(f"{tag} is not a valid tag.") return None lhs = tag rhs = convertToConstantTerm(lhs) eq = Equation(lhs, rhs) subst = SubstituteTerm() state = Unification_state([eq], subst) subst = apply_rules(state) if (trivial_subst(subst)): print("The authenticity property is satisfied.") else: print("The authenticity property is violated.")
def __init__(self, moo_name: str, schedule_name: str = 'every'): chaining_function = MOO.find(moo_name) if chaining_function is None: raise ValueError(f"Mode of operation {moo_name} is not found.") self.chaining_function: Callable = chaining_function schedule = MOO_Schedule.find(schedule_name) if schedule is None: raise ValueError(f"Schedule of name {schedule_name} is not found.") self.schedule: Callable = schedule self.substitutions: SubstituteTerm = SubstituteTerm() self.iteration: int = 0 # TODO: Maybe consider generating a uuid for the IV, so that # it is different across sessions? self.nonces: List[Constant] = [Constant("r")] self.plain_texts: List[Term] = list() self.cipher_texts: List[Term] = list() self.stopped = False
def fix(sigma, constraint): #Fix sigma by instantiating it so that it becomes a P-unifier. print("fixing:") print(sigma) if (contains_original_var_only(sigma)): return sigma else: #Prepare for a list of saturation problem problem_list = [] for var in sigma.domain(): if (is_original_variable(var)): problem = deepcopy(constraint[var]) problem.append(ConstrainedTerm(var * sigma, SubstituteTerm())) problem_list.append(problem) #Solve them list_of_substitutions = [] for p in problem_list: saturation_result = saturate(p) substs = [] for sat_res in saturation_result: if (sat_res.term == Zero()): substs.append(sat_res.constraint) list_of_substitutions.append(substs) #Combine the resulting substitutions results = combine_list_of_substitutions(list_of_substitutions) if (results == []): return None #Find a substitution that can be fixed recursively # Use it to instantiate sigma, return the result for tau in results: result = fix(tau, instantiate_a_constraint(constraint, tau)) if (result != None): return sigma * result return None
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 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)}.")
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 add_empty_subst_to_initial_list(lst): new_list = [] for item in lst: new_list.append(ConstrainedTerm(item, SubstituteTerm())) return new_list
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)