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 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 ex4_gen(p, i): if i == 0: return Constant('r' + str(p)) else: return xor( xor(FuncTerm(f, [ex4_gen(p, i - 1)]), FuncTerm(f, [FuncTerm(f, [ex4_gen(p, i - 1)])])), Variable("x" + str(p) + str(i)))
def make_a_decision(bad_var_term_pairs, p_unif_problem): constraints = p_unif_problem.constraints disequations = p_unif_problem.disequations for (var, term) in bad_var_term_pairs: if (isinstance(term, Constant)): continue if (isinstance(term, FuncTerm) and term.function.symbol == "h"): continue if (isinstance(term, FuncTerm) and term.function.symbol == "f"): #fix it using the Prev rule term = convert_to_xorterm(term) for prev_term in constraints[var]: if (not isinstance(prev_term, Variable)): candidate_equation = Equation(xor(term, prev_term), Zero()) if (consistent_with_diseqs(candidate_equation, disequations)): return candidate_equation if (isinstance(term, Variable)): #set it to zero if consistent candidate_equation = Equation(term, Zero()) if (consistent_with_diseqs(candidate_equation, disequations)): return candidate_equation if (isinstance(term, XORTerm)): #if it has a variable x in it, set x to some subset #otherwise, use the cancel rule to cancel two terms terms = term.arguments terms = simplify(terms) terms = list(map(convert_to_xorterm, terms)) vars = get_vars_in_list(terms) if (len(vars) == 0): pairs = findsubsets(terms, 2) #print(pairs) for pair in pairs: (left, right) = pair left = convert_to_xorterm(left) right = convert_to_xorterm(right) candidate_equation = Equation(xor(left, right), Zero()) if (consistent_with_diseqs(candidate_equation, disequations)): return candidate_equation else: for var in vars: subsets = powerset(terms) for subset in subsets: xor_terms = list(set(subset) | set([var])) if (len(xor_terms) == 1): left_side = xor_terms[0] else: left_side = xor(*xor_terms) candidate_equation = Equation(left_side, Zero()) if (consistent_with_diseqs(candidate_equation, disequations)): return candidate_equation return None
def cipher_block_chaining(iteration, nonces, P, C): """Cipher Block Chaining""" IV = nonces[0] f = Function("f", 1) i = iteration - 1 if i == 0: return f( xor(P[0], IV) ) return f( xor(P[i], C[i-1]) )
def abc_h_identity(iteration, nonces, P, C): """ABC H IDENTITY""" IV = nonces[0] i = iteration - 1 f = Function("f", 1) Q = {} if i > 0: Q[i] = _calcQI(i, nonces, P, C) Q[i-1] = _calcQI(i - 1, nonces, P, C) keystream = f(IV) if i == 0: return IV return xor(f(xor(Q[i], C[i-1])),Q[i-1])
def propogating_cbc(iteration, nonces, P, C): """Propogating Cipher Block Chaining""" IV = nonces[0] f = Function("f", 1) i = iteration - 1 if i == 0: return f( xor(P[0], IV) ) return f( xor( P[i], P[i - 1], C[i - 1] ) )
def _calcQI(i, nonces, P, C): h = Function("h", 1) if i == 0: return P[0] return xor( P[i], h(_calcQI(i - 1, nonces, P, C)) )
def convert_to_xorterm(t): # convert all the sub-XORTerms to xor terms if (isinstance(t, Constant)): return t if (isinstance(t, Variable)): return t if (is_xor_term(t)): new_arguments = list(map(convert_to_xorterm, t.arguments)) return xor(*new_arguments) if (isinstance(t, FuncTerm) and not is_xor_term(t)): new_arguments = list(map(convert_to_xorterm, t.arguments)) return FuncTerm(t.function, new_arguments) if (isinstance(t, XORTerm)): new_arguments = list(map(convert_to_xorterm, t.arguments)) if (len(new_arguments) == 1): return new_arguments[0] else: return xor(*new_arguments)
def symbolic_cbc_gen(session_label, block_label): a = Constant("1") p = Constant(session_label) i = Constant(block_label) cInner = FuncTerm(c, [p, i, a]) x = Variable("xpi") return xor(FuncTerm(f, [cInner]), x)
def output_feedback(iteration, nonces, P, _): """Output Feedback""" IV = nonces[0] i = iteration - 1 f = Function("f", 1) keystream = f(IV) for _ in range(i): keystream = f(keystream) return xor(P[i], keystream)
def hash_cbc(iteration, nonces, P, C): """Hash Cipher Block Chaining""" IV = nonces[0] f = Function("f", 1) h = Function("h", 1) i = iteration - 1 if i == 0: return f( xor( h(IV), P[0] ) ) return f( xor( h(C[i - 1]), P[i] ) )
def invert_simple(term): """ Algorithm to get the plaintext P_{i} out of a MOO. Works by moving up in the DAG from the plaintext variable Applying the reverse transformation as it moves along f -> f^{-1} xor(P, x) -> xor(P, xor(x, x)) -> P Assumptions: - Previous plaintexts aren't in the term - The plaintext is only in one position """ inverse_term = Variable("x") transforms = [] # Create a TermDAG and check to see # that the plaintext is only in one position. d = TermDAG(term) assert Counter(d.leaves())[_P] == 1 # Move up the DAG starting from the plaintext # leaf and add inverse operations to the # transforms list. current_term = deepcopy(_P) parent_term = list(d.parents(current_term)) while parent_term != []: parent_term = parent_term[0] if parent_term.function == _f: transforms.append((_finv, )) elif parent_term.function == xor: # Cancel out the xor by xoring with # the other argument again. if parent_term.arguments[0] == current_term: transforms.append((xor, parent_term.arguments[1])) else: transforms.append((xor, parent_term.arguments[0])) else: raise ValueError("A function other than f or xor detected") current_term = deepcopy(parent_term) parent_term = list(d.parents(parent_term)) # Construct inverse term for transform in reversed(transforms): if transform[0] == _finv: inverse_term = _finv(inverse_term) else: # Assume xor inverse_term = xor(inverse_term, transform[1]) return inverse_term
def cipher_feedback(iteration, nonces, P, C): """Cipher Feedback""" IV = nonces[0] i = iteration - 1 f = Function("f", 1) if i == 0: return f(IV) return xor( f(C[i-1]), P[i] )
def symbolic_ex4_gen(session_label, block_label): a = Constant("1") p = Constant(session_label) i = Constant(block_label) cInner = FuncTerm(c, [p, i, a]) x = Variable("xpi") fSummandOne = FuncTerm(f, [cInner]) fSummandTwo = FuncTerm(f, [FuncTerm(f, [cInner])]) return xor(fSummandOne, fSummandTwo, x)
def convertToConstantTerm(t): if (isinstance(t, IndexedVariable)): return t.convertToConstant() if (isinstance(t, Constant) or isinstance(t, Variable)): return t if (isinstance(t, FuncTerm) and t.function.symbol == "xor"): new_arguments = list(map(convertToConstantTerm, t.arguments)) return xor(*new_arguments) if (isinstance(t, FuncTerm) and t.function.symbol != "xor"): new_arguments = list(map(convertToConstantTerm, t.arguments)) return FuncTerm(t.function, new_arguments) else: print("error in convertToConstantTerm") return None
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 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 moo_invert(K: Set[Term], nonces: Set[Constant], S: int, P: Set[Term]) -> bool: """ Given a set K of MOO interactions and max bound for the depth of a term, state whether or not a MOO term is invertible. Note, this is intentionally more general than is needed for the strict invert problem for MOO programs and plaintext/var/con. It can also consider terms and some constructed terms. """ # Compute K* k_star = K | nonces # From K* construct C* c_star = deepcopy(k_star) current_length = 0 while len(c_star) != current_length: current_length = len(c_star) temp = set() for t_1 in c_star: # Definition 11 (2a) if isinstance(t_1, FuncTerm) and str(t_1.function) == 'f': # Apply f inverse temp.update({t_1.arguments[0]}) # Definition 11 (2b) for t_2 in c_star: new_term_b = xor(t_1, t_2) if depth(new_term_b) <= S: temp.add(new_term_b) # Definition 11 (2c) new_term_c = _f(t_1) if depth(new_term_c) <= S: temp.add(new_term_c) c_star.update(temp) # Check to see if the plaintext block is in any of the ground terms in c_star # xor library automatically maps terms to their ground terms #return _P in c_star print(c_star) if P.issubset(c_star): return True else: return False
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 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)
#Example of using the deducible function from symcollab.algebra import Function, Variable, Constant from symcollab.moe.invertibility import deducible x = Constant("x") f = Function("f", 2) p = Constant("p_i") deducible(f(x, p), {x}) deducible(f(x, p), {}) #example of using invertibility with MOO security check from symcollab.algebra import Constant, Variable from symcollab.moe.program import MOOProgram from symcollab.moe.check import moo_check from symcollab.Unification.constrained.xor_rooted_unif import XOR_rooted_security from symcollab.Unification.constrained.p_unif import p_unif result = moo_check('cipher_block_chaining', "every", p_unif, 2, True, True) print(result.invert_result) #example of using invertibility by itself, not how it's intended to be used #but can be done for testing from symcollab.moe.invertibility import InvertMOO from symcollab.xor import xor f = Function("f", 1) x = Variable("x") IV = Constant("IV") C1 = xor(x, IV) print("MOO Invertible?", InvertMOO(C1, "x", [IV], IV, True))
from symcollab.moe.syntactic_check import moo_depth_random_check, \ moo_has_random, moo_f_depth f = Function("f", 1) r = Constant("r") x = Variable("x") x2 = Variable("x2") zero = Zero() print("Example 1.") t1 = moo_f_depth(f(f(r))) print(f"f_depth(f(f(r))) = {t1}") print("") print("Example 2.") t2 = moo_f_depth(xor(f(f(r)), f(r))) print(f"f_depth(f(f(r)) ⊕ f(r)) = {t2}") print("") print("Example 3.") possible_subs1 = dict() possible_subs1[x] = [r, f(r)] t3 = moo_f_depth(xor(f(r), x), possible_subs1) print(f"If {x} maps to {r} or {f(r)}...") print(f"f_depth(f(r) ⊕ x) = {t3}") print("") print("Example 4.") possible_subs2 = dict() possible_subs2[x] = [r, zero] t4 = moo_f_depth(xor(f(r), x), possible_subs2)
def cbc_gen(p, i): if i == 0: return Constant('r' + str(p)) else: return xor(FuncTerm(Function("f", 1), [cbc_gen(p, i - 1)]), Variable("x" + str(p) + str(i)))
#!/usr/bin/env python3 """ A thought experiment on how to define recursive MOOs. """ from symcollab.algebra import Constant, Function, Variable from symcollab.rewrite import RewriteRule, RewriteSystem, normal from symcollab.xor import xor from symcollab.theories.nat import Nat C = Function("C", 1, domain_sort=Nat.sort) P = Function("P", 1, domain_sort=Nat.sort) f = Function("f", 1) IV = Constant("IV") n = Variable("n", sort=Nat.sort) r0 = RewriteRule(C(Nat.zero), IV) rn = RewriteRule(C(Nat.S(n)), f(xor(P(Nat.S(n)), C(n)))) moo_system = RewriteSystem({r0, rn}) print("Cipher Block Chaining:", moo_system) three = Nat.from_int(3) print("Simplified form of the 3rd ciphertext:", normal(C(three), moo_system)[0])