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 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 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)
class Field(TheorySystem): """ A field is a ring with every nonzero element having a multiplicative inverse. """ add = Function("add", 2) mul = Function("mul", 2) negate = Function("neg", 1) inverse = Function("inv", 1) zero = Constant("0") unity = Constant("1")
def symbolic_moo_gen(session_label, block_label): c = Function("C", 3) p = Constant(session_label) i = Constant(block_label) a = Constant("1") pList = [ Variable(f"x{session_label}{block_label}"), Variable(f"x{session_label}{block_label}"), Variable(f"x{session_label}{block_label}") ] cList = [c(p, i, a), c(p, i, a), c(p, i, a)] return program.chaining_function(3, [0], pList, cList)
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)
class Boolean(TheorySystem): trueb = Constant("true") falseb = Constant("false") @classmethod def from_bool(cls, x: bool) -> Term: """Converts a bool to a Boolean.""" return deepcopy(Boolean.trueb if x else Boolean.falseb) @classmethod def to_bool(cls, x: Term) -> bool: """Converts a Boolean to an bool.""" if not isinstance(x, FuncTerm) or x != Boolean.trueb or x != Boolean.falseb: raise ValueError("to_bool function expects a simplified Boolean.") return x == Boolean.trueb
def _temporary_parser(moo_string: str) -> Term: """ A temporary parser to parse a user supplied cryptographic mode of operation. This function is limited in what it can parse. """ parser = Parser() parser.add(Function("f", 1)) parser.add(xor) parser.add(Variable("P[i]")) parser.add(Variable("C[i]")) parser.add(Variable("C[i-1]")) parser.add(Constant("r")) parser.add(Constant("P[0]")) return parser.parse(moo_string)
def freeze(term): """ Converts all the variables inside a term into constants. Parameters ---------- term : Term The term in which to turn the variables into constants. Examples -------- >>> from symcollab.algebra import Function, Variable >>> from symcollab.rewrite import freeze >>> f = Function("f", 1) >>> x = Variable("x") >>> freeze(f(x)) f(x) """ if isinstance(term, Variable): return Constant(term.symbol, term.sort) elif isinstance(term, FuncTerm): arguments = list(term.arguments) for i, t in enumerate(arguments): arguments[i] = freeze(t) term.arguments = arguments return term
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 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()
class Group(TheorySystem): """ A group is a set G with an operation op that contain the closure and associative properties, as well as contains an identity and inverse element. """ identity = Constant("e") op = Function("op", 2) inverse = Function("inv", 1)
class Ring(TheorySystem): """ A ring is an abelian group with another binary operation that is associative, distributive over the abelian operation, and has an ientity element. """ add = Function("add", 2) mul = Function("mul", 2) negate = Function("neg", 1) zero = Constant("0")
def __init__(self, max_history: int = 2, max_f_depth: int = 3): self.max_history = max_history self.max_f_depth = max_f_depth self.f = Function("f", 1) self.r = Constant("r") # Only one nonce currently self.tree: List[List[Term]] = [[ self.f(MOOGenerator._P(0)), xor(self.r, MOOGenerator._P(0)) ]] self.branch_iter: Iterator[Term] = iter( self.tree[0]) # Where we are at the branch
def _conditions_met(self, term: Term) -> bool: """Given a term, state whether the conditions are met.""" if _f_depth(term) > self.max_f_depth: return False if self.requires_chaining and not _satisfies_chaining( term, self.max_history): return False if self.requires_iv and Constant("r") not in term: return False # Passes all conditions return True
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 __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 deducible(term: Term, known_constants: Set[Constant]): """ Implementation of Lemma 9 from the Indocrypt paper Parameters ========== term: Term we want to deduce from known_constants: Constants known by a valid decryptor Examples ======== >>> from symcollab.algebra import * >>> from symcollab.moe.invertibility import deducible >>> x = Constant("x") >>> p = Constant("p_i") >>> f = Function("f", 2) >>> deducible(f(x, p), {x}) True >>> deducible(f(x, p), {}) False """ # First check if term is ground if len(get_vars(term)) > 0: return False # Get constants from term without p_i p_i = Constant("p_i") constants_from_term = get_constants(term, unique=True) constants_from_term.difference_update({p_i}) # Check that all constants other than p_i are known if len(constants_from_term.difference(known_constants)) > 0: return False # Make sure p_i only appears once if count_occurence(p_i, term) != 1: return False # Passes all the criteria return True
def wrap(cls): cls.sort = Sort(cls.__name__) cls.rules = deepcopy(cls.rules) cls.definitions = deepcopy(cls.definitions) for name, term in cls.__dict__.items(): # Ignore private, already defined, and custom methods if '_' in name \ or name in TheorySystem.__dict__ \ or (callable(term) and not isinstance(term, Function)): continue if isinstance(term, Constant): if term.sort is not None and term.sort != cls.sort: raise ValueError( f"Constant {term} is of sort '{term.sort}' \ which is not the class name '{class_sort}'.") setattr(cls, name, Constant(term.symbol, sort=cls.sort)) elif isinstance(term, Function): if term.domain_sort is not None and term.domain_sort != cls.sort: raise ValueError(f"Function {term} has the domain sort \ set to '{term.domain_sort}' \ which is not the class name '{class_sort}'.") range_sort = cls.sort if term.range_sort is None else term.range_sort setattr( cls, name, Function(term.symbol, term.arity, domain_sort=cls.sort, range_sort=range_sort)) else: raise ValueError(f"Variable '{name}' is of invalid type \ '{type(term)}' inside an inductive class. (Constant, Function)" ) _system_sort_map[cls.sort] = cls return cls
class Nat(TheorySystem): zero = Constant("0") S = Function("S", 1) @classmethod def from_int(cls, x: int) -> Term: """Converts an integer to a nat.""" result = deepcopy(Nat.zero) for _ in range(x): result = Nat.S(result) return result @classmethod def to_int(cls, x: Term) -> int: """Converts a nat to an int.""" if not isinstance(x, FuncTerm) or x.sort != Nat.sort: raise ValueError("to_int function expects a nat.") if x == Nat.zero: return 0 if isinstance(x, FuncTerm) and x.function == Nat.S: return 1 + cls.to_int(x.arguments[0]) raise ValueError("to_int: Only accepts signature {0, S}")
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)
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()
#!/usr/bin/env python3 from symcollab.algebra import Function, Variable, Constant from symcollab.xor.xorhelper import * from symcollab.xor.structure import * f = Function("f", 1) x = Variable("x") y = Variable("y") z = Variable("z") x1 = Variable("x1") x2 = Variable("x2") x3 = Variable("x3") a = Constant("a") b = Constant("b") c = Constant("c") d = Constant("d") ze = Zero() t1 = xor(x, f(y), f(x1)) t2 = xor(y, f(z), f(x2)) t3 = xor(z, f(x), f(x3)) eq1 = Equation(ze, t1) eq2 = Equation(ze, t2) eq3 = Equation(ze, t3) equations = Equations([eq1, eq2, eq3]) #t1 = f(xor(x, y)) #t2 = x #eq1 = Equation(t1, t2) #equations = Equations([eq1])
#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))
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 from symcollab.algebra import Constant, Function, Variable from symcollab.rewrite import RewriteRule, RewriteSystem, narrow, is_finite, Variants f = Function("f", 2) x = Variable("x") a = Constant("a") b = Constant("b") r = RewriteRule(f(x, x), x) r2 = RewriteRule(f(a, x), b) print("Rewrite Rule 1:", r) print("Rewrite Rule 2:", r2) term = f(a, f(b, b)) rs = RewriteSystem({r, r2}) vt = Variants(term, rs) print("Variants of", term, ":", list(vt)) print("Variants Finite?", is_finite(vt, -1)) print("Rewrite rule from", term, "to", f(a, b), narrow(term, f(a,b), rs, -1))
def convertToConstant(self): s = self.symbol newSymbol = s[0].lower() + s[1:] return Constant(newSymbol)
#!/usr/bin/env python3 from symcollab.algebra import Constant, Function, Parser, Variable f = Function("f", 2) a = Constant("a") x = Variable("x") p = Parser() p.add(f) p.add(x) p.add(a) print("Parsing f(x,a) is successful?", p.parse("f(x,a)") == f(x,a)) print("Parsing f(f(x,a),f(x,a)) is successful?", p.parse("f(f(x,a),f(x,a))") == f(f(x,a), f(x,a)))
#!/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])