//! Determine whether to show an event int isEventInteresting (const System sys, const Roledef rd) { if (switches.human) { if (rd->type != CLAIM) { return 1; } else { // A claim if (only_claim_label == NULL) { return 1; } else { if (isTermEqual (only_claim_label, rd->label)) { return 1; } } } return 0; } else { return 1; } }
/** *@return Adds f(x)=y to an existing function f. If f is NULL, a function is created. If x is already in the domain, the value is replaced. */ Termmap termmapSet (const Termmap f, const Term x, const int y) { Termmap fscan; //! Determine whether term already occurs fscan = f; while (fscan != NULL) { if (isTermEqual (x, fscan->term)) { //! Is the result correct already? if (fscan->result != y) fscan->result = y; return f; } fscan = fscan->next; } //! Not occurred yet, make new node fscan = makeTermmap (); fscan->term = x; fscan->result = y; fscan->next = f; return fscan; }
/** * Currently, inequality constraints are encoded using "NotEqual" claims. * * Here we check that their arguments have not become equal. If they are not * equal, there always exists a solution in which the values are different. The * solution generated by the algorithm that grounds the trace (for * visualisation) yields a compatible solution. * * Return true if okay - constraints can be met * Return false if not okay - at least one constraint violated * * Note that this function performs its own proof output if needed. * This allows it to pinpoint the exact constraint that is violated. * * Speed: this is certainly not the most efficient way to solve this. We are * looping over all regular events, even if there are not negative constraints * at all. Instead, we could simply collect a list of all negative constraints, * which would speed up iterating over it. */ int inequalityConstraints (const System sys) { int run; for (run = 0; run < sys->maxruns; run++) { if (sys->runs[run].protocol != INTRUDER) { int e; Roledef rd; rd = sys->runs[run].start; for (e = 0; e < sys->runs[run].step; e++) { if (rd->type == CLAIM) { // It's a claim if (isTermEqual (rd->claiminfo->type, CLAIM_Notequal)) { // TODO ASSERT: Message should be a pair for NotEqual claims if (isTermEqual (TermOp1 (rd->message), TermOp2 (rd->message))) { // Inequality violated, no solution exists that makes them inequal anymore. if (switches.output == PROOF) { indentPrint (); eprintf ("Pruned because the pattern violates an inequality constraint based on the term "); termPrint (TermOp1 (rd->message)); eprintf (".\n"); } return false; } } } rd = rd->next; } } } return true; }
/** * Return true if should be pruned * * The termlist 'agentrole' contains subsequences of length 2, the first of which is a agent name, and the second is the role it is performing. * The idea is that once the agent name is assigned, it should not occur again for a different role. * E.g. [ alice, initiator, bob, responder, charlie, ... ] */ int multipleRolePrune (const System sys) { Termlist agentrole; int run; agentrole = NULL; for (run = 0; run < sys->maxruns; run++) { Protocol p; p = sys->runs[run].protocol; if ((p != INTRUDER) && (!isHelperProtocol (p))) { Term rolename; Term agent; Termlist tl; rolename = sys->runs[run].role->nameterm; agent = agentOfRun (sys, run); // Does this agent already occur yet in the list? for (tl = agentrole; tl != NULL; tl = (tl->next)->next) { if (isTermEqual (agent, tl->term)) { if (!isTermEqual (rolename, (tl->next)->term)) { // Same agent, but different role! This is not allowed. termlistDelete (agentrole); // cleanup return true; } } } // Does not occur yet, so add // Note we add the elements in front, so we need to reverse the order agentrole = termlistPrepend (agentrole, rolename); agentrole = termlistPrepend (agentrole, agent); } } termlistDelete (agentrole); return false; }
/** *@return Yields f(x), or -1 when it is not present. */ int termmapGet (Termmap f, const Term x) { while (f != NULL) { if (isTermEqual (x, f->term)) return f->result; f = f->next; } return -1; }
//! Given a list of label infos, yield the correct one or NULL Labelinfo label_find (List labellist, const Term label) { Labelinfo linfo; int label_find_scan (void *data) { Labelinfo linfo_scan; linfo_scan = (Labelinfo) data; if (isTermEqual (label, linfo_scan->label)) { linfo = linfo_scan; return 0; } else { return 1; } }
/** * Try to determine the most general unifier of two terms. * Resulting termlist must be termlistDelete'd. * *@return Returns a list of variables, that were previously open, but are now closed * in such a way that the two terms unify. Returns \ref MGUFAIL if it is impossible. * The termlist should be deleted. * * @TODO this code should be removed, as it duplicates 'unify' code, and is * ill-suited for adaption later on with multiple unifiers. */ Termlist termMguTerm (Term t1, Term t2) { /* added for speed */ t1 = deVar (t1); t2 = deVar (t2); if (t1 == t2) return NULL; if (!(hasTermVariable (t1) || hasTermVariable (t2))) { if (isTermEqual (t1, t2)) { return NULL; } else { return MGUFAIL; } } /* * Distinguish a special case where both are unbound variables that will be * connected, and I want to give one priority over the other for readability. * * Because t1 and t2 have been deVar'd means that if they are variables, they * are also unbound. */ if (realTermVariable (t1) && realTermVariable (t2) && goodsubst (t1, t2)) { /* Both are unbound variables. Decide. * * The plan: t1->subst will point to t2. But maybe we prefer the other * way around? */ if (preferSubstitutionOrder (t2, t1)) { Term t3; // Swappy. t3 = t1; t1 = t2; t2 = t3; } t1->subst = t2; #ifdef DEBUG showSubst (t1); #endif return termlistAdd (NULL, t1); } /* symmetrical tests for single variable. */ if (realTermVariable (t2)) { if (termSubTerm (t1, t2) || !goodsubst (t2, t1)) return MGUFAIL; else { t2->subst = t1; #ifdef DEBUG showSubst (t2); #endif return termlistAdd (NULL, t2); } } if (realTermVariable (t1)) { if (termSubTerm (t2, t1) || !goodsubst (t1, t2)) return MGUFAIL; else { t1->subst = t2; #ifdef DEBUG showSubst (t1); #endif return termlistAdd (NULL, t1); } } /* left & right are compounds with variables */ if (t1->type != t2->type) return MGUFAIL; /* identical compounds */ /* encryption first */ if (realTermEncrypt (t1)) { Termlist tl1, tl2; tl1 = termMguTerm (TermKey (t1), TermKey (t2)); if (tl1 == MGUFAIL) { return MGUFAIL; } else { tl2 = termMguTerm (TermOp (t1), TermOp (t2)); if (tl2 == MGUFAIL) { termlistSubstReset (tl1); termlistDelete (tl1); return MGUFAIL; } else { return termlistConcat (tl1, tl2); } } } /* tupling second non-associative version ! TODO other version */ if (isTermTuple (t1)) { Termlist tl1, tl2; tl1 = termMguTerm (TermOp1 (t1), TermOp1 (t2)); if (tl1 == MGUFAIL) { return MGUFAIL; } else { tl2 = termMguTerm (TermOp2 (t1), TermOp2 (t2)); if (tl2 == MGUFAIL) { termlistSubstReset (tl1); termlistDelete (tl1); return MGUFAIL; } else { return termlistConcat (tl1, tl2); } } } return MGUFAIL; }
/** * Try to determine the most general unifier of two terms, if so calls function. * * int callback(Termlist, *state) * * The callback receives a list of variables, that were previously open, but are now closed * in such a way that the two terms unify. * * The callback must return true for the iteration to proceed: if it returns false, a single call would abort the scan. * The return value shows this: it is false if the scan was aborted, and true if not. */ int unify (Term t1, Term t2, Termlist tl, int (*callback) (), void *state) { /* added for speed */ t1 = deVar (t1); t2 = deVar (t2); if (t1 == t2) { return callback (tl, state); } if (!(hasTermVariable (t1) || hasTermVariable (t2))) { // None has a variable if (isTermEqual (t1, t2)) { // Equal! return callback (tl, state); } else { // Can never be fixed, no variables return true; } } /* * Distinguish a special case where both are unbound variables that will be * connected, and I want to give one priority over the other for readability. * * Because t1 and t2 have been deVar'd means that if they are variables, they * are also unbound. */ if (realTermVariable (t1) && realTermVariable (t2) && goodsubst (t1, t2)) { /* Both are unbound variables. Decide. * * The plan: t1->subst will point to t2. But maybe we prefer the other * way around? */ if (preferSubstitutionOrder (t2, t1)) { Term t3; // Swappy. t3 = t1; t1 = t2; t2 = t3; } return callsubst (callback, state, tl, t1, t2); } /* symmetrical tests for single variable. */ if (realTermVariable (t2)) { if (termSubTerm (t1, t2) || !goodsubst (t2, t1)) return true; else { return callsubst (callback, state, tl, t2, t1); } } if (realTermVariable (t1)) { if (termSubTerm (t2, t1) || !goodsubst (t1, t2)) return true; else { return callsubst (callback, state, tl, t1, t2); } } /* left & right are compounds with variables */ if (t1->type != t2->type) return true; /* identical compound types */ /* encryption first */ if (realTermEncrypt (t1)) { struct state_mgu_tmp tmpstate; tmpstate.oldstate = state; tmpstate.oldcallback = callback; tmpstate.unifyt1 = TermOp (t1); tmpstate.unifyt2 = TermOp (t2); return unify (TermKey (t1), TermKey (t2), tl, unify_callback_wrapper, &tmpstate); } /* tupling second non-associative version ! TODO other version */ if (isTermTuple (t1)) { struct state_mgu_tmp tmpstate; tmpstate.oldstate = state; tmpstate.oldcallback = callback; tmpstate.unifyt1 = TermOp2 (t1); tmpstate.unifyt2 = TermOp2 (t2); return unify (TermOp1 (t1), TermOp1 (t2), tl, unify_callback_wrapper, &tmpstate); } return true; }
/** * When something is pruned because of this function, the state space is still * considered to be complete. * *@returns true iff this state is invalid because of a theorem */ int prune_theorems (const System sys) { List bl; int run; // Check all types of the local agents according to the matching type if (!checkAllSubstitutions (sys)) { if (switches.output == PROOF) { indentPrint (); eprintf ("Pruned because some local variable was incorrectly substituted.\n"); } return true; } // Prune if agents are disallowed from performing multiple roles if (switches.oneRolePerAgent != 0) { if (multipleRolePrune (sys)) { if (switches.output == PROOF) { indentPrint (); eprintf ("Pruned because an agent may not perform multiple roles.\n"); } return true; } } // Prune if any initiator run talks to itself /** * This effectively disallows Alice from talking to Alice, for all * initiators. We still allow it for responder runs, because we assume the * responder is not checking this. */ if (switches.initUnique) { if (selfInitiators (sys) > 0) { // XXX TODO // Still need to fix proof output for this // // Pruning because some agents are equal for this role. return true; } } if (switches.respUnique) { if (selfResponders (sys) > 0) { // XXX TODO // Still need to fix proof output for this // // Pruning because some agents are equal for this role. return true; } } if (switches.roleUnique) { if (!agentsUniqueRoles (sys)) { if (switches.output == PROOF) { indentPrint (); eprintf ("Pruned because agents are not performing unique roles.\n"); } return true; } } /* The semantics imply that create event chose agent names, i.e., the range of rho is a subset of Agent. For chosen name attacks we may want to loosen that. However, this requires inserting receive events for the non-actor role variables of responders, and we don't have that yet, so technically this is a bug. Don't use. */ if (switches.chosenName) { // Check if all actors are agents for responders (initiators come next) run = 0; while (run < sys->maxruns) { if (!sys->runs[run].role->initiator) { Term actor; actor = agentOfRun (sys, run); if (!goodAgentType (actor)) { if (switches.output == PROOF) { indentPrint (); eprintf ("Pruned because the actor "); termPrint (actor); eprintf (" of run %i is not of a compatible type.\n", run); } return true; } } run++; } // Prune wrong agents type for initators if (!initiatorAgentsType (sys)) { if (switches.output == PROOF) { indentPrint (); eprintf ("Pruned: an initiator role does not have the correct type for one of its agents.\n"); } return true; } } else { // Prune wrong agents type for runs if (!allAgentsType (sys)) { if (switches.output == PROOF) { indentPrint (); eprintf ("Pruned: some run does not have the correct type for one of its agents.\n"); } return true; } } // Check if the actors of all other runs are not untrusted if (sys->untrusted != NULL) { int run; run = 1; while (run < sys->maxruns) { if (sys->runs[run].protocol != INTRUDER) { if (sys->runs[run].rho != NULL) { Term actor; actor = agentOfRun (sys, run); if (actor == NULL) { error ("Agent of run %i is NULL", run); } if (!isAgentTrusted (sys, actor)) { if (switches.output == PROOF) { indentPrint (); eprintf ("Pruned because the actor of run %i is untrusted.\n", run); } return true; } } else { Protocol p; globalError++; eprintf ("error: Run %i: ", run); role_name_print (run); eprintf (" has an empty agents list.\n"); eprintf ("protocol->rolenames: "); p = (Protocol) sys->runs[run].protocol; termlistPrint (p->rolenames); eprintf ("\n"); error ("Aborting."); globalError--; return true; } } run++; } } // Check for redundant patterns { if (!non_redundant ()) { if (switches.output == PROOF) { indentPrint (); eprintf ("Pruned because the pattern is redundant.\n"); } return true; } } // Check for violation of inequality constraints if (!inequalityConstraints (sys)) { // Prune, because violated return true; } /* * Check for correct orderings involving local constants * * TODO: Clarify how this works with agent name variables in a non strict-typed setting. */ if (!(switches.experimental & 8)) { if (!correctLocalOrder (sys)) { if (switches.output == PROOF) { indentPrint (); eprintf ("Pruned because this does not have the correct local order.\n"); } return true; } } /** * Check whether the bindings are valid */ bl = sys->bindings; while (bl != NULL) { Binding b; b = bl->data; // Check for "Hidden" interm goals //! @todo in the future, this can be subsumed by adding TERM_Hidden to the hidelevel constructs if (termInTerm (b->term, TERM_Hidden)) { // Prune the state: we can never meet this if (switches.output == PROOF) { indentPrint (); eprintf ("Pruned because intruder can never construct "); termPrint (b->term); eprintf ("\n"); } return true; } if (switches.experimental & 4) { // Check for SK-type function occurrences //!@todo Needs a LEMMA, although this seems to be quite straightforward to prove. // The idea is that functions are never sent as a whole, but only used in applications. //! @todo Subsumed by hidelevel lemma later if (isTermFunctionName (b->term)) { if (!inKnowledge (sys->know, b->term)) { // Not in initial knowledge of the intruder if (switches.output == PROOF) { indentPrint (); eprintf ("Pruned because the function "); termPrint (b->term); eprintf (" is not known initially to the intruder.\n"); } return true; } } } // Check for encryption levels /* * if (switches.match < 2 *! @todo Doesn't work yet as desired for Tickets. Prove lemma first. */ if (switches.experimental & 2) { if (!hasTicketSubterm (b->term)) { if (term_encryption_level (b->term) > max_encryption_level) { // Prune: we do not need to construct such terms if (switches.output == PROOF) { indentPrint (); eprintf ("Pruned because the encryption level of "); termPrint (b->term); eprintf (" is too high.\n"); } return true; } } } // To be on the safe side, we currently limit the encryption level. /** * This is valid *only* if there are no ticket-type variables. */ if (term_encryption_level (b->term) > max_encryption_level) { // Prune: we do not need to construct such terms if (sys->hasUntypedVariable) { sys->current_claim->complete = false; } if (switches.output == PROOF) { indentPrint (); eprintf ("Pruned because the encryption level of "); termPrint (b->term); eprintf (" is too high.\n"); } return true; } /** * Prune on the basis of hidelevel lemma */ if (hidelevelImpossible (sys, b->term)) { // Prune: we do not need to construct such terms if (switches.output == PROOF) { indentPrint (); eprintf ("Pruned because the hidelevel of "); termPrint (b->term); eprintf (" is impossible to satisfy.\n"); } return true; } bl = bl->next; } /* check for singular roles */ run = 0; while (run < sys->maxruns) { if (sys->runs[run].role->singular) { // This is a singular role: it therefore should not occur later on again. int run2; Term rolename; rolename = sys->runs[run].role->nameterm; run2 = run + 1; while (run2 < sys->maxruns) { Term rolename2; rolename2 = sys->runs[run2].role->nameterm; if (isTermEqual (rolename, rolename2)) { // This is not allowed: the singular role occurs twice in the semitrace. // Thus we prune. if (switches.output == PROOF) { indentPrint (); eprintf ("Pruned because the singular role "); termPrint (rolename); eprintf (" occurs more than once in the semitrace.\n"); } return true; } run2++; } } run++; } return false; }