def decompose(state): #Try applying the "decompose" rule #If successful, returns "True" and the new state #Otherwise, returns "False" and the original state eqs = state.equations subst = state.substitution first = eqs[0] remaining = eqs[1:] lhs = first.left_side rhs = first.right_side flag = False new_eqs = [] if (isinstance(lhs, Constant) and isinstance(rhs, Constant) and lhs == rhs): return (True, Unification_state(remaining, subst)) if (topSymbol(lhs, 'e') and topSymbol(rhs, 'e')): flag = True new_eqs += [Equation(lhs._arguments[0], rhs._arguments[0])] new_eqs += [Equation(lhs._arguments[1], rhs._arguments[1])] if (topSymbol(lhs, 'd') and topSymbol(rhs, 'd')): flag = True new_eqs += [Equation(lhs._arguments[0], rhs._arguments[0])] new_eqs += [Equation(lhs._arguments[1], rhs._arguments[1])] if (topSymbol(lhs, 'n') and topSymbol(rhs, 'n')): flag = True new_eqs += [Equation(lhs._arguments[0], rhs._arguments[0])] return (flag, Unification_state(new_eqs + remaining, subst))
def test_check_xor_structure(): f = Function("f", 1) a = Constant("a") b = Constant("b") c = Constant("c") x = Variable("x") z = Zero() func = FuncTerm(f, [a]) func1 = FuncTerm(f, [b]) func2 = FuncTerm(f, [c]) func3 = FuncTerm(f, [x]) func4 = xor(func, func1) func5 = xor(func2, func3) eq1 = Equation(func, b) eq2 = Equation(func3, c) eq3 = Equation(func5, z) eq4 = Equation(xor(func2, func3), z) eq5 = Equation(xor(func4, func5), z) topf: Set[Equation] = {eq1, eq2, eq3, eq5} print("Testing pick_f with equation set: ", topf) print("Result pick_f: ", pick_f(topf)) print()
def check_redundancy(rule: RewriteRule, redundancy_rules: List[RewriteRule]): """ UNFINISHED Checks redundancy of a rule against a set of redundancy rules. """ for redundancy_rule in redundancy_rules: hypothesis_unifiers = unif( {Equation(redundancy_rule.hypothesis, rule.hypothesis)}) conclusion_unifiers = unif( {Equation(redundancy_rule.conclusion, rule.conclusion)}) if hypothesis_unifiers is not False and conclusion_unifiers is not False: # If all substitutions in conclusion_unifiers present in hypothesis unifiers, return True if all(((var, term) in hypothesis_unifiers.subs for var, term in zip(*conclusion_unifiers.subs))): return True for position in fpos(rule.hypothesis)[1:]: unifiers = unif({ Equation(redundancy_rule.hypothesis, get_sub_term(rule.hypothesis, position)) }) if unifiers is not False: return True return False
def test_pick_fail(): c = Function("C", 3) f = Function("f", 1) a = Constant("a") b = Constant('1') e = Constant("e") p = Constant('p') q = Constant('q') i = Constant('i') j = Constant('j') x = Variable("x") z = Zero() func = FuncTerm(f, [a]) func2 = FuncTerm(f, [e]) func_pi = FuncTerm(c, [p, i, b]) func_qj = FuncTerm(c, [q, j, b]) eq1 = Equation(func, z) eq2 = Equation(func_qj, x) eq3 = Equation(xor(func_pi, func), z) eq4 = Equation(xor(xor(func_pi, func), func2), z) topf: Set[Equation] = {eq1, eq4} print("Testing pick_fail with ", topf) new_set = pick_fail(topf, cbc_gen) print("Result: ", new_set) print() topf: Set[Equation] = {eq1, eq2, eq3} print("Testing pick_fail with ", topf) new_set = pick_fail(topf, ex4_gen) print("Result: ", new_set) print()
def apply(self, state): #Apply a rule to some equation #Assume that the N-Decomposition rule is applicable. #Returns two states equations = state.equations.contents disequations = state.disequations.contents substs = state.substitution (eq_index, first_term, second_term) = self.get_equation_and_two_terms(state) equations1 = equations equations2 = deepcopy(equations) equation_to_be_processed = equations1[eq_index] del equations1[eq_index] #Remove first_term and second_term from new_equation. new_term = xor(equation_to_be_processed.left_side, equation_to_be_processed.right_side) term_list = xor_to_list(new_term) new_lhs = [] for t in term_list: if (t == first_term or t == second_term): continue else: new_lhs.append(t) if (len(new_lhs) != 0): new_equation = Equation(list_to_xor(new_lhs), Zero()) else: new_equation = Equation(Zero(), Zero()) equations1.append(new_equation) unifier = unif({Equation(first_term, second_term)}) #equations1.append(Equation(xor(first_term.arg, second_term.arg), Zero())) #if((first_term == second_term) or (unifier.domain() != [])): # unifiable = True #else: # unifiable = False if (unifier == False): unifiable = False else: unifiable = True disequations1 = disequations disequations2 = deepcopy(disequations) new_disequation = Disequation(first_term, second_term) disequations2.append(new_disequation) state2 = XOR_proof_state(Equations(equations2), Disequations(disequations2), substs) if (unifiable): state1 = XOR_proof_state( apply_sub_to_equations(Equations(equations1), unifier), apply_sub_to_disequations(Disequations(disequations1), unifier), substs * unifier) return (unifiable, state1, state2) else: return (unifiable, state, state2)
def overlap(rule1, rule2, rs, position): """ UNFINISHED Overlaps a rule at a position with a second rule, then reduces the outcome against an initial set of rules. """ rule1_copy = deepcopy(rule1) rule2_copy = deepcopy(rule2) right = get_sub_term(rule1_copy.conclusion, position) left = rule2_copy.hypothesis unifiers = unif({Equation(left, right)}) print("Left", left) print("Right", right) print(unifiers) if unifiers is False: return False new_left = deepcopy(rule1_copy.hypothesis) new_left = new_left * unifiers new_right = deepcopy(rule1_copy.conclusion) new_right = replace(new_right, position, rule2_copy.conclusion) new_right = new_right * unifiers new_right = normal(new_right, rs)[0] return RewriteRule(new_left, new_right)
def purify_an_equation(eq, eqs): (purified_lhs, eqs) = purify_a_term(eq.left_side, eqs) #eqs = eqs + eqs1 (purified_rhs, eqs) = purify_a_term(eq.right_side, eqs) #eqs = eqs + eqs2 new_equation = Equation(purified_lhs, purified_rhs) return (new_equation, eqs)
def normalize_an_equation(eq): if is_zero(eq.right_side): lhs = eq.left_side else: lhs = xor(eq.left_side, eq.right_side) lst = xor_to_list(lhs) lst = simplify(lst) new_lhs = list_to_xor(lst) new_rhs = Zero() new_eq = Equation(new_lhs, new_rhs) return new_eq
def test_elimf(): f = Function("f", 1) xo = Function("f", 2) z = Zero() c = Constant("c") x = Variable("x") b = Variable("b") func = FuncTerm(f, [x]) func2 = FuncTerm(f, [z]) func3 = FuncTerm(xo, [c, b]) eq1 = Equation(func, c) eq2 = Equation(xor(func, func3), z) eq3 = Equation(b, func) eq4 = Equation(func2, z) topf: Set[Equation] = {eq1, eq2, eq3, eq4} print("Testing elim_f with ", topf) new_set = elim_f(topf) print("Result ", new_set) print()
def p_syntactic(l: Term, r: Term, constraints: Dict[Variable, List[Term]]): """ p_syntactic is syntactic unification with the assurance that the unifier satisfies the constraints given. """ sigma = unif({Equation(l, r)}) if sigma == False: return sigma sigma_domain, sigma_range = zip(*sigma.subs) for x, y in zip(sigma_domain, sigma_range): if isinstance(y, FuncTerm) and y not in constraints[x]: return False return sigma
def apply(self, state): eq = self.is_applicable(state) first = eq.left_side.arguments[0] second = eq.left_side.arguments[1] sigma = unif({Equation(first, second)}) if (sigma == False): return (False, None) else: new_eqs = apply_sub_to_equations(state.equations, sigma) new_eqs = normalize_equations(new_eqs) state.substitution = state.substitution * sigma return (True, XOR_proof_state(new_eqs, state.disequations, state.substitution))
def test_pick_c(): c = Function("C", 3) f = Function("f", 1) a = Constant("a") b = Constant('1') p = Constant('p') q = Constant('q') i = Constant('i') j = Constant('j') x = Variable("x") z = Zero() func = FuncTerm(f, [a]) func_pi = FuncTerm(c, [p, i, b]) func_qj = FuncTerm(c, [q, j, b]) eq1 = Equation(func, z) eq2 = Equation(func_qj, x) eq3 = Equation(xor(func_pi, func), 0) topf: Set[Equation] = {eq1, eq2, eq3} print("Testing pick_c with ", topf)
def _match(self, term: Term) -> Term: """Attempts to rewrite the root term with the rewrite rule. Returns the same term if rewriting is not possible""" # Change common variables in RewriteRule if they exist overlaping_vars = _getOverlapVars(term, self.hypothesis, self.conclusion) while overlaping_vars: self.hypothesis, self.conclusion = _changeVars( overlaping_vars, term, self.hypothesis, self.conclusion) overlaping_vars = _getOverlapVars(term, self.hypothesis, self.conclusion) # Perform matching and substitution frozen_term = freeze(term) sigma = unify({Equation(self.hypothesis, frozen_term)}) return self.conclusion * sigma if sigma is not False else None
def test_occurs(): f = Function("f", 1) g = Function("g", 1) c = Function("C", 3) p = Constant('p') q = Constant('q') i = Constant('i') z = Variable("z") x = Variable("x") y = Variable("y") b = Variable("b") a = Variable("a") cpi = FuncTerm(c, [p, i, Constant("1")]) fcpi = FuncTerm(f, [cpi]) e1 = Equation(cpi, fcpi) e2 = Equation(x, FuncTerm(f, [g(x)])) e3 = Equation(x, FuncTerm(f, [b])) occ = {e1, e2, e3} print("Testing occurs check with ", occ) print("new set: ", occurs_check(occ)) print()
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 split(state): #Try applying the "split" rule #This rule applied if an equation is of the form: s_1 xor e(...) = t_1 xor e(...) #or s_1 xor d(...) = t_1 xor d(...) #If successful, returns "True" and the new state #Otherwise, returns "False" and the original state #Example: s_1 xor e(...) = t_1 xor e(...); id ==> s_1 = t_1, e(...) xor e(...); id #Example: s_1 = t_1; id (not applicable) 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 = containsDorE(lhs_summands) if (applicable): (e_term1, others1) = split_terms(lhs_summands) (e_term2, others2) = split_terms_wrt_term(rhs_summands, e_term1) eq1 = Equation(e_term1, e_term2) if (len(others1) == 1): eq2 = Equation(*others1, *others2) else: eq2 = Equation(xor(*others1), xor(*others2)) remaining_eqs.append(eq1) remaining_eqs.append(eq2) return (applicable, Unification_state(remaining_eqs, subst)) else: return (applicable, state)
def test_elimc(): c = Function("C", 3) p = Constant('p') q = Constant('q') i = Constant('i') j = Constant('j') a = Constant('1') x = Variable("x") z = Zero() func_pi = FuncTerm(c, [p, i, a]) func_qj = FuncTerm(c, [q, j, a]) eq1 = Equation(xor(func_pi, func_qj), z) eq2 = Equation(xor(func_qj, func_pi), z) eq3 = Equation(func_pi, x) topf: Set[Equation] = {eq1, eq2, eq3} print("Testing elim_c with ", topf) new_set = elim_c(topf) print("Result: ", new_set) print() b = Constant('2') func_pi = FuncTerm(c, [p, i, a]) func_qj = FuncTerm(c, [q, j, b]) eq1 = Equation(xor(func_pi, func_qj), z) eq2 = Equation(xor(func_qj, func_pi), z) topf: Set[Equation] = {eq1, eq2} print("Testing elim_c with ", topf) new_set = elim_c(topf) print("Result: ", new_set) print()
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 find_collision(cipher_text1: Term, cipher_text2: Term, constraints: Dict[Variable, List[Term]], unif_algo: Callable = p_unif) -> Optional[SubstituteTerm]: """ Sets up a unification problem between two ciphertexts in order to see if there is a possible collision given some constraints. """ if unif_algo == p_unif: unifiers = unif_algo(Equations([Equation(cipher_text1, cipher_text2)]), constraints) elif unif_algo == XOR_rooted_security: unifiers = unif_algo([cipher_text1, cipher_text2], constraints).solve() elif unif_algo == p_syntactic: unifiers = unif_algo(cipher_text1, cipher_text2, constraints) else: raise ValueError("Unification algorithm provided is not supported.") return unifiers
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 resolve(constrained_t1, constrained_t2): #Takes two constrained terms t1[sigma] and t2[tau], apply resolution to them, #returns a list of all possible results #For example, if t1 is f(x) + a[y |-> z] and t2 is f(a) + b[y |-> a], #then the result is a + b[y |-> a] t1 = convert_to_XORTerm(constrained_t1.term) constraint1 = constrained_t1.constraint t2 = convert_to_XORTerm(constrained_t2.term) constraint2 = constrained_t2.constraint constraint12 = combine_two_substitutions(constraint1, constraint2) if (constraint12 == []): return [] results1 = get_resolution_term_and_remaining_term(t1) results2 = get_resolution_term_and_remaining_term(t2) results = [] for (term1, remaining_t1) in results1: for (term2, remaining_t2) in results2: eq = Equation(convert_to_xorterm(term1), convert_to_xorterm(term2)) eqs = Equations([eq]) #print("solving:") #print(eq) xor_unifiers = xor_unification(eqs) #print("xor unifiers:") #for xor_u in xor_unifiers: # print(xor_u) for unifier in xor_unifiers: for cons12 in constraint12: constraint123 = combine_two_substitutions(unifier, cons12) for cons123 in constraint123: remaining_t1 = convert_to_xorterm(remaining_t1) remaining_t2 = convert_to_xorterm(remaining_t2) r1 = remaining_t1 * cons123 r2 = remaining_t2 * cons123 r1 = convert_to_XORTerm(r1) r2 = convert_to_XORTerm(r2) constraint_term = ConstrainedTerm( combine_two_XOR_Terms(r1, r2), cons123) results.append(constraint_term) return results
def name_xor_terms(t, eqs): # Purify arguments # Name top-level xor-subterms # Return (renamed_term, a set of equations) if is_zero(t): return (t, eqs) elif (isinstance(t, Constant)): return (t, eqs) elif (isinstance(t, Variable)): return (t, eqs) elif (is_xor_term(t)): (term1, eqs) = purify_a_term(t.arguments[0], eqs) #eqs = eqs + equation1 (term2, eqs) = purify_a_term(t.arguments[1], eqs) #eqs = eqs + equation2 #equations = equation1 + equation2 name = look_up_a_name(xor(term1, term2), eqs) if (name != None): return (name, eqs) else: global variable_counter variable_counter += 1 new_variable = Variable("N" + str(variable_counter)) eqs.append(Equation(new_variable, xor(term1, term2))) return (new_variable, eqs) elif (isinstance(t, FuncTerm)): terms = [] for arg in t.arguments: (term, eqs) = name_xor_terms(arg, eqs) #eqs = eqs + equations terms.append(term) return (FuncTerm(t.function, terms), eqs) else: print("error") return None
a = Constant("a") b = Constant("b") c = Constant("c") d = Constant("d") zero = Zero() #The CBC mode is modeled as: [c, x1, t1, x2, t2, ......] #where t1 = f(xor(c, x1) and t2 = f(xor(x2, t1)) t1 = f(xor(c, x1)) t2 = f(xor(x2, t1)) #The constraints are modeled using a dictionary in Python. #So x1 can take any term from constraints[x1] and terms built up using "h" and "xor" #x2 is similar constraints1 = {x1: [c, zero], x2: [c, zero, x1, t1]} #trying to unify t1 and t2 eq1 = Equation(t1, t2) eqs1 = Equations([eq1]) result = p_unif(eqs1, constraints1) print("Here are the P-unifiers:") for r in result: print(r) #Here is another mode, modeled as [a, x1, f(a), x2, f(x2), x3, b, x4, f(xor(h(f(x1)), f(x4), f(b)))] '''' t3 = f(x2) t4 = f(xor(h(f(x1)), f(x4), f(b))) eq2 = Equation(t3, t4) eqs2 = Equations([eq2])
#!/usr/bin/env python3 from symcollab.algebra import Function, Variable, Constant, Equation from symcollab.Unification.unif import unif # Setting up terms f = Function("f", 2) g = Function("g", 1) x = Variable("x") y = Variable("y") z = Variable("z") a = Constant("a") b = Constant("b") # applying unification #example 1: unifiable unif({Equation(f(x, y), f(a, b))}) #example 2: simple function clash unif({Equation(f(x, y), g(z))}) #example 3: function clash unif({Equation(f(x, x), f(g(y), a))}) #example 4: occurs check unif({Equation(f(x, y), f(g(x), a))}) #example 5: unifiable unif({Equation(f(z, z), f(g(f(x, y)), g(f(a, b))))})
from symcollab.algebra import Constant, Function, Variable, Equation from symcollab.Unification.syntactic_ac_unification import * #Setup the variables and AC function f = Function("f", 2) x = Variable("x") y = Variable("y") z = Variable("z") a = Constant("a") b = Constant("b") x1 = Variable("x1") y1 = Variable("y1") z1 = Variable("z1") #Example 1 e = Equation(f(x, y), f(x1, y1)) U = {e} sol = synt_ac_unif(U) #For a single solution # also sol = synt_ac)unif(U, True) sol = synt_ac_unif(U, False) # For all solutions #Example 2 e = Equation(f(x, x), f(y, y)) U = {e} sol = synt_ac_unif(U) #Example 3 e2 = Equation(f(x, x), f(f(y, y), f(z, z))) U1 = {e2} sol = synt_ac_unif(U1)
from symcollab.algebra import Constant, Equation, Function, Sort, \ SubstituteTerm, Variable from symcollab.algebra.dag import TermDAG # Setting up terms f = Function("f", 2) g = Function("g", 2) x = Variable("x") y = Variable("y") z = Variable("z") a = Constant("a") b = Constant("b") c = Constant("c") # Simple Equation Printing e1 = Equation(x, a) e2 = Equation(f(Constant("2"), Constant("2")), Constant("4")) print(e1) print(e2) # Simple sort test reals = Sort("reals") 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.")
def apply_sub_to_equation(eq, sub): lhs = eq.left_side rhs = eq.right_side return Equation(lhs * sub, rhs * sub)
#eac_unif_ex.py from symcollab.algebra import Function, Equation, Variable from symcollab.Unification.eac_unif import eac_unif exp = Function("exp", 2) x = Variable("x") y = Variable("y") w = Variable("w") t = exp(x, y) t2 = exp(x, w) e1 = Equation(t, t2) U = set() U.add(e1) z = Variable("z") g = Function("g", 2) t3 = g(z, z) t4 = g(w, w) e2 = Equation(t3, t4) U.add(e2) eac_unif(U) u = Variable("u") v = Variable("v") t3 = exp(v, w) t4 = g(x, y) e3 = Equation(u, t3) e4 = Equation(u, t4) U = set() U.add(e3) U.add(e4) eac_unif(U)
from symcollab.Unification.ac_unif import ac_unify f = Function("f", 2) g = Function("g", 1) x = Variable("x") y = Variable("y") z = Variable("z") a = Constant("a") b = Constant("b") t = f(x, x) s = f(f(y, y), x) v = f(y, y) w = f(z, z) e1 = Equation(s, t) e2 = Equation(v, w) U = set() U.add(e1) U.add(e2) U2 = set() U2.add(e1) ac_unify(U) ac_unify(U2) from symcollab.algebra import Constant, Function, Variable, Equation from symcollab.Unification.ac_unif import ac_unify f = Function("f", 2)