/*!\file modelchecker.c * \brief The main procedures guiding the (optimized) traversal of the state space. * * This file implements various traversal methods through the state space. */ #include #include #include #include "substitution.h" #include "knowledge.h" #include "system.h" #include "debug.h" #include "modelchecker.h" #include "report.h" #include "memory.h" #include "match_basic.h" #include "match_clp.h" #include "output.h" #include "tracebuf.h" #include "attackminimize.h" #include "claim.h" #include "switches.h" /* A model checker. Really. */ extern Term CLAIM_Secret; extern Term CLAIM_Nisynch; extern Term CLAIM_Niagree; extern Term CLAIM_Empty; /* Some forward declarations. */ __inline__ int traverseSimple (const System oldsys); __inline__ int traverseNonReads (const System oldsys); __inline__ int traversePOR4 (const System oldsys); __inline__ int traversePOR5 (const System oldsys); __inline__ int traversePOR6 (const System oldsys); __inline__ int traversePOR7 (const System oldsys); __inline__ int traversePOR8 (const System oldsys); int propertyCheck (const System sys); int executeTry (const System sys, int run); int claimSecrecy (const System sys, const Term t); int violateClaim (const System sys, int length, int claimev, Termlist reqt); Termlist secrecyUnfolding (Term t, const Knowledge know); /* Main code. */ void statePrint (const System sys) { int i, s; Roledef rd; indent (); printf ("state %i: ", sys->step); for (i = 0; i < sys->maxruns; i++) { s = 0; rd = runPointerGet (sys, i); while (rd != NULL) { rd = rd->next; s++; } printf ("%i ", s); } printf (" - phase %i, done %i", sys->PORphase, sys->PORdone); printf ("\n"); } //! Scenario selection makes sure implicit and explicit chooses are selected first. /** * This help function traverses any chooses first. */ __inline__ int traverse_chooses_first (const System sys) { int run_scan; for (run_scan = 0; run_scan < sys->maxruns; run_scan++) { Roledef rd_scan; rd_scan = runPointerGet (sys, run_scan); if (rd_scan != NULL && // Not empty run rd_scan == sys->runs[run_scan].start && // First event rd_scan->type == READ) // Read { if (executeTry (sys, run_scan)) return 1; } } return 0; } //! Main traversal call. /** * Branches into submethods. */ int traverse (const System sys) { /* maybe chooses have precedence over _all_ methods */ if (switches.chooseFirst) { if (traverse_chooses_first (sys)) return 1; } /* branch for traversal methods */ switch (switches.traverse) { case 1: return traverseSimple (sys); case 2: return traverseNonReads (sys); case 3: case 4: case 5: case 6: case 7: error ("%i is an obsolete traversal method.", switches.traverse); case 8: return traversePOR4 (sys); case 9: return traversePOR5 (sys); case 10: return traversePOR6 (sys); case 11: return traversePOR7 (sys); case 12: return traversePOR8 (sys); default: error ("%i is NOT an existing traversal method.", switches.traverse); } } //! Progress counters to next step. /** * Does not really execute anything, it's just bookkeeping, progressing * counters and such. * *@returns If it returns TRUE, explore. If false, don't traverse. */ int executeStep (const System sys, const int run) { Roledef runPoint; runPoint = runPointerGet (sys, run); #ifdef DEBUG if (DEBUGL (3)) { indent (); printf ("exec: "); roledefPrint (runPoint); printf ("#%i\n", run); } #endif sys->runs[run].step++; runPointerSet (sys, run, runPoint->next); /* store knowledge for this step */ (sys->step)++; sys->traceKnow[sys->step] = sys->know; /* check for properties */ propertyCheck (sys); /* set indent for printing */ indentSet (sys->step); /* hmmm, but what should it return if not exploring? */ if (!sys->explore) return 0; /* we want to explore it, but are we allowed by pruning? */ if (sys->step >= sys->maxtracelength) { /* cut off traces that are too long */ #ifdef DEBUG if (DEBUGL (4)) { indent (); printf ("trace cut off.\n"); if (DEBUGL (5)) { (sys->step)--; tracePrint (sys); (sys->step)++; } } #endif return 0; } /* we will explore this state, so count it. */ sys->states = statesIncrease (sys->states); /* what about scenario exploration? */ if (switches.scenario && sys->step + 1 > switches.scenarioSize) { /* count states within scenario */ sys->statesScenario = statesIncrease (sys->statesScenario); } /* show progression */ if (switches.reportStates > 0) { sys->interval = statesIncrease (sys->interval); if (!statesSmallerThan (sys->interval, (unsigned long int) switches.reportStates)) { globalError++; sys->interval = STATES0; eprintf ("States "); statesFormat (sys->states); eprintf (" \r"); globalError--; } } /* store new node numbder */ sys->traceNode[sys->step] = sys->states; /* the construction below always assumes MAX_GRAPH_STATES to be smaller than the unsigned long it, which seems realistic. */ if (switches.output == STATESPACE && statesSmallerThan (sys->states, MAX_GRAPH_STATES)) { /* display graph */ graphNode (sys); } return 1; } //! Determine end of run after removing end reads, and optionally claims. Roledef removeDangling (Roledef rd, const int killclaims) { Roledef rdkill; rdkill = rd; while (rd != NULL) { if (rd->type == SEND || (!killclaims && rd->type == CLAIM)) rdkill = rd; rd = rd->next; } /* remove after rdkill */ return rdkill; } /** * Determine for a roledef that is instantiated, the uninteresting ends bits. * *@todo "What is interesting" relies on the fact that there are only secrecy, sychnr and agreement properties. */ Roledef removeIrrelevant (const System sys, const int run, Roledef rd) { if (untrustedAgent (sys, sys->runs[run].agents)) { // untrusted, so also remove claims return removeDangling (rd, 1); } else { // trusted, so only remove reads return removeDangling (rd, 0); } } //! Unblock any waiting sends of my type. /** * Note that the caller must ensure that rd->forbidden is restored afterwards. *\sa tryChoiceSend() */ void unblock_synchronising_labels (const System sys, const int run, const Roledef rd) { if (rd->type != READ || rd->internal) return; if (!inTermlist (sys->synchronising_labels, rd->label)) return; // This read possibly unblocks other sends int run_scan; for (run_scan = 0; run_scan < sys->maxruns; run_scan++) { if (run_scan != run) { Roledef rd_scan; rd_scan = sys->runs[run_scan].index; if (rd_scan != NULL && rd_scan->type == SEND && rd_scan->forbidden != NULL && isTermEqual (rd_scan->label, rd->label) && isTermEqual (rd_scan->message, rd->message) && isTermEqual (rd_scan->from, rd->from) && isTermEqual (rd_scan->to, rd->to)) { rd_scan->forbidden = NULL; } } } return; } //! Explores the system state given by the next step of a run. /** Grandiose naming scheme (c) sjors dubya. * * After an event was instantiated, this function is called to explore the * remainder of the system. * * Note that some additional pruning is done, so this function * also determines the enabledness of the event that was just instantiated. * *@returns 1 (true) if the event was enabled. *\sa match_basic(),executeTry() */ int explorify (const System sys, const int run) { Roledef rd; int flag; int myStep; Roledef roleCap, roleCapPart; Knowledge forbiddenBuffer; rd = runPointerGet (sys, run); myStep = sys->runs[run].step; roleCap = NULL; if (rd == NULL) { error ("Trying to progress completed run!\n"); } flag = 0; /** * -------------------------------------------- * Is the event really enabled? * Typically, a read event is only instantiated * at this point, so we can only now do some * instantiation related determinings. * -------------------------------------------- */ /* * Special checks after (implicit) choose events; always first in run reads. */ if (myStep == 0 && rd->type == READ) { int rid; if (inTermlist (sys->untrusted, agentOfRun (sys, run))) { /* this run is executed by an untrusted agent, do not explore */ return 0; } /* executed by trusted agent */ /* Special check 1: if agents have been instantiated in such a way that no more claims in any run * need to be evaluated, then we can skip * further traversal. */ //!@todo This implementation relies on the fact that there are only secrecy, synchr and agreement properties. if (switches.pruneNomoreClaims && sys->secrets == NULL) { /* there are no remaining secrecy claims to be checked */ Roledef rdscan; int validclaim; rid = 0; validclaim = 0; /* check for each run */ while (rid < sys->maxruns) { /* are claims in this run evaluated anyway? */ if (!untrustedAgent (sys, sys->runs[rid].agents)) { /* possibly claims to be checked in this run */ rdscan = runPointerGet (sys, rid); while (rdscan != NULL) { if (rdscan->type == CLAIM) { /* force abort of loop */ validclaim = 1; rdscan = NULL; rid = sys->maxruns; } else { rdscan = rdscan->next; } } } rid++; } if (validclaim == 0) { /* no valid claims, abort */ return 0; } } /* Special check 2: Symmetry reduction on chooses. * If the run we depend upon has already been activated (otherwise warn!) check for instance ordering */ if (switches.agentSymmetries && sys->runs[run].prevSymmRun != -1) { /* there is such a run on which we depend */ int ridSymm; ridSymm = sys->runs[run].prevSymmRun; if (sys->runs[ridSymm].step == 0) { /* * dependency run was not chosen yet, so we can't do anything now */ // warning ("Symmetrical run dependency #%i (for run #%i) has not chosen yet!", ridSymm, run); } else { /* dependent run has chosen, so we can compare */ int order; order = termlistOrder (sys->runs[run].agents, sys->runs[ridSymm].agents); if (order < 0) { /* we only explore the other half */ return 0; } if (order == 0 && switches.reduceClaims) { /* identical run; only the first would be checked for a claim */ /* so we cut off this run, including claims, turning it into a dummy run */ roleCap = removeDangling (rd, 1); } } } /* Special check 3: if after choosing, this run is untrusted and ends on (read|skippedclaim)*, we can remove that part already. */ if (switches.reduceEndgame && roleCap == NULL) roleCap = removeIrrelevant (sys, run, rd); /* Special check x: if all agents in each run send only encrypted stuff, and all agents are trusted, * there is no way for the intruder to learn anything else than encrypted terms, so secrecy claims will not * be violated anymore if they contain no terms that are encrypted with such keys */ //!@todo For now, there is no check that the runs only send publicly encrypted stuff! Just an assumption to be made true using static analysis. /* rid = 0; while (rid < sys->maxruns) { if (!untrustedAgent (sys, sys->runs[rid].agents)) { } rid++; } */ } /* * Special check b1: symmetry reduction part II on similar read events for equal roles. */ if (switches.readSymmetries) { if (sys->runs[run].firstNonAgentRead == myStep) { /* Apparently, we have a possible ordering with our symmetrical friend. * Check if it has progressed enough, and has the same agents. */ int ridSymm; if (rd->type != READ) { error ("firstNonAgentRead is not a read?!"); } ridSymm = sys->runs[run].prevSymmRun; if (isTermlistEqual (sys->runs[run].agents, sys->runs[ridSymm].agents)) { /* same agents, so relevant */ if (myStep > 0 && sys->runs[ridSymm].step < myStep) { // warning ("Symmetrical firstread dependency #%i (for run #%i) has not chosen yet!", ridSymm, run); } else { if (sys->runs[ridSymm].step <= myStep) { // warning ("Symmetrical firstread dependency #%i (for run #%i) has not read it's firstNonAgentRead %i yet, as it is only at %i!", ridSymm, run, myStep, sys->runs[ridSymm].step); } else { /* read was done, so we can compare them */ int i; Roledef rdSymm; rdSymm = sys->runs[ridSymm].start; i = myStep; while (i > 0) { rdSymm = rdSymm->next; i--; } /* rdSymm now points to the instance of the symmetrical read */ i = termOrder (rdSymm->message, rd->message); if (i < 0) { /* only explore symmetrical variant */ return 0; } } } } } } /* Special check b2: symmetry order reduction. * * Concept: when there are two identical runs w.r.t. agents, we can make sure one goes before the other. * Depends on prevSymm, skipping chooses even. */ if (switches.orderSymmetries && myStep == sys->runs[run].firstReal) { if (sys->runs[run].prevSymmRun != -1) { /* there is such a run on which we depend */ int ridSymm; ridSymm = sys->runs[run].prevSymmRun; /* equal runs? */ if (isTermlistEqual (sys->runs[run].agents, sys->runs[ridSymm].agents)) { /* so, we have an identical partner */ /* is our partner there already? */ if (sys->runs[ridSymm].step <= myStep) { /* not yet there, this is not a valid exploration */ /* verify !! */ return 0; } } } } /** * Final special check; we must be sure that chooses have been done. Only * necessary for scenario != 0. * * Note: any choose selection after this would result in empty scenarios, so this * should be the last special check. */ if (switches.scenario != 0) { /* two variants. If scenario size is 0, we operate on the old method involving chooses */ if (switches.scenarioSize == 0) { /* only after chooses */ if (myStep == 0 && rd->type == READ) { if (run == sys->lastChooseRun) { /* We are just after the last choose instance */ /* count this instance */ if (sys->countScenario < INT_MAX) { sys->countScenario++; } /* If we are displaying scenarios, print it */ if (switches.output == SCENARIOS) { printf ("%i\t", sys->countScenario); scenarioPrint (sys); printf ("\n"); } /* If it is not the selected one, abort */ if (switches.scenario != sys->countScenario) { /* this branch is not interesting */ /* unfortunately, it is also not drawn in the state graph because of this */ if (switches.output == STATESPACE) { graphScenario (sys, run, rd); } /* act as if executed, but don't really explore it. */ return 1; } } } } else { /* scenario size is not zero */ //!@todo Optimization: if the good scenario is already traversed, other trace prefixes need not be explored any further. if (sys->step + 1 == switches.scenarioSize) { /* Now, the prefix has been set. Count it */ if (sys->countScenario < INT_MAX) { sys->countScenario++; } if (switches.output == SCENARIOS) { /* apparently we want the output */ int index; eprintf ("%i\t", sys->countScenario); index = 0; while (index < switches.scenarioSize) { roledefPrint (sys->traceEvent[index]); eprintf ("#%i; ", sys->traceRun[index]); index++; } eprintf ("\n"); } /* Is this the selected one? */ if (switches.scenario != sys->countScenario) { /* unfortunately, it is also not drawn in the state graph because of this */ if (switches.output == STATESPACE) { graphScenario (sys, run, rd); } /* act as if executed, but don't really explore it. */ return 1; } } } } /** * -------------------------------------------- * Now we assume the event is indeed enabled ! * -------------------------------------------- */ /* (Possibly) do some local damage */ // Unblock any sends forbiddenBuffer = rd->forbidden; if (sys->synchronising_labels != NULL) { unblock_synchronising_labels (sys, run, rd); } // Cap roles if (roleCap != NULL) { roleCapPart = roleCap->next; roleCap->next = NULL; } /* And explore the resulting system */ if (executeStep (sys, run)) { /* traverse the system after the step */ flag = traverse (sys); } /* restore executeStep "damage" */ runPointerSet (sys, run, rd); // reset rd pointer sys->runs[run].step = myStep; // reset local index sys->step--; indentSet (sys->step); /* restore local damage */ rd->forbidden = forbiddenBuffer; if (roleCap != NULL) { roleCap->next = roleCapPart; } return 1; // The event was indeed enabled (irrespective of traverse!) } //! Simple nondeterministic traversal. __inline__ int traverseSimple (const System sys) { int run; int flag = 0; for (run = 0; run < (sys->maxruns); run++) { if (runPointerGet (sys, run) != NULL) { flag = 1; executeTry (sys, run); } } if (!flag) { /* trace was not succesful */ } return flag; } /** * ----------------------------------------------------- * * Traversal Methods * * ----------------------------------------------------- */ /* * Some assistance macros */ #define predRead(sys,rd) ( rd->type == READ && !rd->internal ) #define isRead(sys,rd) ( rd != NULL && predRead(sys,rd) ) #define nonRead(sys,rd) ( rd != NULL && !predRead(sys,rd) ) /* * nonReads * * Do a certain type of action first, i.e. that which satisfies nonRead(System, * Roledef). Use the inverse of this predicate to detect the other type of * event. */ __inline__ int nonReads (const System sys) { /* all sends first, then simple nondeterministic traversal */ int run; Roledef rd; /* check for existence of executable sends */ for (run = 0; run < (sys->maxruns); run++) { rd = runPointerGet (sys, run); if (nonRead (sys, rd)) { return executeTry (sys, run); } } return 0; } /* * First traverse any non-reads, then non-deterministically the reads. */ __inline__ int traverseNonReads (const System sys) { if (nonReads (sys)) return 1; else return traverseSimple (sys); } //! Execute a send /** *\sa unblock_synchronising_labels() */ __inline__ int tryChoiceSend (const System sys, const int run, const Roledef rd) { int flag; flag = 0; if (rd->forbidden == NULL) { /* this is allowed. So we either try it, or we try it later (if blocking) */ /* Note that a send in executetry will always succeed. */ /* 1. Simply try */ flag = executeTry (sys, run); /* 2. Postpone if synchonisable */ if (flag && inTermlist (sys->synchronising_labels, rd->label)) { /* This is supposed to be blocked, so we do so */ /* It will possibly be unblocked by a corresponding read event, * the actual code would be in explorify, post instantiation of the read event. */ if (switches.clp) { block_clp (sys, run); } else { block_basic (sys, run); } } } return flag; } //! Execute a read in the knowledge postponing way, on which the partial order reduction for secrecy depends. __inline__ int tryChoiceRead (const System sys, const int run, const Roledef rd) { int flag; flag = 0; /* the sendsdone check only prevent * some unneccessary inKnowledge tests, * and branch tests, still improves * about 15% */ if (sys->knowPhase > rd->knowPhase) { /* apparently there has been a new knowledge item since the * previous check */ /* implicit check for enabledness */ flag = executeTry (sys, run); /* if it was enabled (flag) we postpone it if it makes sense * to do so (hasVariable, non internal) */ if (flag && hasTermVariable (rd->message) && !rd->internal) { int stackKnowPhase = rd->knowPhase; rd->knowPhase = sys->knowPhase; if (switches.clp) { block_clp (sys, run); } else { block_basic (sys, run); } rd->knowPhase = stackKnowPhase; } } return flag; } //! Try to execute the event at the roledef. Returns true iff it was enabled, and thus explored. /** * Note that rd should not be NULL */ __inline__ int tryChoiceRoledef (const System sys, const int run, const Roledef rd) { int flag; #ifdef DEBUG if (rd == NULL) error ("tryChoiceRoledef should not be called with a NULL rd pointer"); #endif flag = 0; switch (rd->type) { case CLAIM: flag = executeTry (sys, run); break; case SEND: flag = tryChoiceSend (sys, run, rd); break; case READ: flag = tryChoiceRead (sys, run, rd); break; default: fprintf (stderr, "Encountered unknown event type %i.\n", rd->type); exit (1); } return flag; } //! Try to execute the event in a given run __inline__ int tryChoiceRun (const System sys, const int run) { Roledef rd; rd = runPointerGet (sys, run); if (rd != NULL) return tryChoiceRoledef (sys, run, rd); else return 0; } //! Yield the last active run in the partial trace, or 0 if there is none. __inline__ int lastActiveRun (const System sys) { if (sys->step == 0) { /* first step, start at 0 */ return 0; } else { /* there was a previous action, start scan from there */ #ifdef DEBUG if (switches.switchP < 100) return sys->traceRun[sys->step - 1] + switches.switchP; #endif return sys->traceRun[sys->step - 1]; } } //! Determine whether a roleevent is a choose event __inline__ int isChooseRoledef (const System sys, const int run, const Roledef rd) { return (rd == sys->runs[run].start && rd->type == READ && rd->internal); } //! Explore possible chooses first __inline__ int tryChoosesFirst (const System sys) { int flag; int run; Roledef rd; flag = 0; for (run = 0; run < sys->maxruns && !flag; run++) { rd = runPointerGet (sys, run); if (isChooseRoledef (sys, run, rd)) flag = tryChoiceRoledef (sys, run, rd); } return flag; } //! Explore left-to-right (from an offset) __inline__ int tryEventsOffset (const System sys, const int offset) { int flag; int run; int i; flag = 0; for (i = 0; i < sys->maxruns && !flag; i++) { run = (i + offset) % sys->maxruns; flag = tryChoiceRun (sys, run); } return flag; } /* * POR4 * * This is the simplified version of the algorithm, to be compared with * the -t7 version. * * Based on some new considerations. */ __inline__ int traversePOR4 (const System sys) { return tryEventsOffset (sys, lastActiveRun (sys)); } /* * POR5 * * POR4 but does chooses first. */ __inline__ int traversePOR5 (const System sys) { if (tryChoosesFirst (sys)) return 1; return tryEventsOffset (sys, lastActiveRun (sys)); } /* * POR6 * * POR5 but has a left-oriented scan instead of working from the current run. */ __inline__ int traversePOR6 (const System sys) { return tryEventsOffset (sys, 0); } /* * POR7 * * Left-oriented scan, to ensure reductions. However, first does all initial actions. */ __inline__ int traversePOR7 (const System sys) { if (tryChoosesFirst (sys)) return 1; tryEventsOffset (sys, 0); } /* * POR8 * * POR6, but tries to continue on the current run first. This turned out to be highly more efficient. */ __inline__ int traversePOR8 (const System sys) { int flag; int run; int last; last = lastActiveRun (sys); flag = tryChoiceRun (sys, last); for (run = 0; run < sys->maxruns && !flag; run++) { if (run != last) flag = tryChoiceRun (sys, run); } return flag; } //! Check for the properties that have lasting effects throughout the trace. /** * Currently, only functions for secrecy. *@returns 1 (true) iff everything is okay, and no attack is found. 0 (false) if an attack is found. */ int propertyCheck (const System sys) { int flag = 1; // default: properties are true, no attack /* for now, we only check secrecy */ if (sys->secrets != NULL) { Termlist scan; scan = sys->secrets; while (scan != NULL) { if (!claimSecrecy (sys, scan->term)) { /* apparently, secrecy of this term was violated */ /* find the violated claim event */ Termlist tl = NULL; int claimev = -1; int i = 0; while (claimev == -1 && i <= sys->step) { if (sys->traceEvent[i]->type == CLAIM && sys->traceEvent[i]->to == CLAIM_Secret) { Termlist tl = secrecyUnfolding (scan->term, sys->know); if (tl != NULL) { /* This was indeed a violated claim */ claimev = i; } } i++; } /* do we have it? */ if (claimev == -1) { /* weird, should not occur */ fprintf (stderr, "Violation, but cannot locate claim.\n"); printf ("A secrecy claim was supposed to be violated on term "); termPrint (scan->term); printf (" but we couldn't find the corresponding claim.\n"); exit (1); } else { /* fine. so it's violated */ violateClaim (sys, sys->step, claimev, tl); termlistDelete (tl); flag = 0; } } scan = scan->next; } } return flag; } /* true iff the term is secret */ int isTermSecret (const System sys, const Term t) { switch (switches.clp) { case 0: /* test for simple inclusion */ if (inKnowledge (sys->know, t)) return 0; if (isTermVariable (t)) { /* it's a variable! */ /* TODO that does not necessarily mean we can choose it, does * it? NO: the rule should be: there is at least one message * in knowledge. We don't check it currently.*/ return 0; } return 1; case 1: /* CLP stuff */ return secret_clp (sys, t); default: return 0; } } /* true iff the claim is valid */ int claimSecrecy (const System sys, const Term t) { int csScan (Term t) { t = deVar (t); if (isTermTuple (t)) return csScan (TermOp1 (t)) && csScan (TermOp2 (t)); else return isTermSecret (sys, t); } if (csScan (t)) return 1; else { /* Not reported anymore here, but only at the end */ // reportSecrecy (sys, t); return 0; } } /* * Unfold the secrecy tuple and construct a list of terms that violate it. */ Termlist secrecyUnfolding (Term t, const Knowledge know) { t = deVar (t); if (isTermTuple (t)) return termlistConcat (secrecyUnfolding (TermOp1 (t), know), secrecyUnfolding (TermOp2 (t), know)); else { if (inKnowledge (know, t)) return termlistAdd (NULL, t); else return NULL; } } /* * for reporting we need a more detailed output of the claims. * Output is a termlist pointer, or -1. * * in: claim roledef, knowledge for which it is violated * * -1 : claim was ignored * NULL : claim is fulfilled (true) * Termlist: claim was violated, termlist terms are know to the intruder. */ Termlist claimViolationDetails (const System sys, const int run, const Roledef rd, const Knowledge know) { if (rd->type != CLAIM) { fprintf (stderr, "Trying to determine details of something other than a claim!\n"); exit (-1); } /* cases */ if (rd->to == CLAIM_Secret) { /* secrecy claim */ if (untrustedAgent (sys, sys->runs[run].agents)) { /* claim was skipped */ return (Termlist) - 1; } else { /* construct violating subterms list */ return secrecyUnfolding (rd->message, know); } } return NULL; } //! A claim was violated. /** * This happens when we violate a claim. * Lots of administration. *@returns True iff explorify is in order. */ int violateClaim (const System sys, int length, int claimev, Termlist reqt) { int flag; Claimlist clinfo; /* default = no adaption of pruning, continue search */ flag = 1; /* Count the violations */ sys->attackid++; sys->failed = statesIncrease (sys->failed); clinfo = sys->traceEvent[claimev]->claiminfo; clinfo->failed = statesIncrease (clinfo->failed); // note: for modelchecking secrecy, this can lead to more fails (at further events in branches of the tree) than claim encounters /* mark the path in the state graph? */ if (switches.output == STATESPACE) { graphPath (sys, length); } /* Copy the current trace to the buffer, if the new one is shorter than the previous one. */ if (sys->attack == NULL || length < sys->attack->reallength) { tracebufDone (sys->attack); sys->attack = tracebufSet (sys, length, claimev); attackMinimize (sys, sys->attack); sys->shortestattack = sys->attack->reallength; /* maybe there is some new pruning going on */ flag = 0; switch (switches.prune) { case 0: flag = 1; break; case 1: break; case 2: sys->maxtracelength = sys->shortestattack - 1; break; } } return flag; } //! Try to execute the next event in a run. /** * One of the core functions of the system. *@returns 0 (false) if the event was not enabled. 1 (true) if there was an enabled instantiation of this event. */ int executeTry (const System sys, int run) { Roledef runPoint; int flag = 0; runPoint = runPointerGet (sys, run); sys->traceEvent[sys->step] = runPoint; // store for later usage, problem: variables are substituted later... sys->traceRun[sys->step] = run; // same if (runPoint == NULL) { #ifdef DEBUG /* warning, ought not to occur */ debug (2, "Trying to activate completed run"); #endif } else { #ifdef DEBUG if (DEBUGL (4)) { indent (); printf ("try: "); roledefPrint (runPoint); printf ("#%i\n", run); } #endif if (runPoint->type == READ) { if (switches.clp) return matchRead_clp (sys, run, explorify); else return matchRead_basic (sys, run, explorify); } if (runPoint->type == SEND) { if (switches.clp) flag = send_clp (sys, run); else flag = send_basic (sys, run); return flag; } /* * Execute claim event */ if (runPoint->type == CLAIM) { /* first we might dynamically determine whether the claim is valid */ if (untrustedAgent (sys, sys->runs[run].agents)) { /* for untrusted agents we check no claim violations at all * so: we know it's okay. */ /* TODO for CLP this doesn't work and call for branching, if the * agent is a variable */ #ifdef DEBUG if (DEBUGL (3)) { indent (); printf ("Skipped claim in untrusted run with agents "); termlistPrint (sys->runs[run].agents); printf ("\n"); } #endif explorify (sys, run); return 1; } /* determine type of claim, and parameters */ #ifdef DEBUG if (DEBUGL (2)) { indent (); printf ("claim: "); roledefPrint (runPoint); printf ("#%i\n", run); } #endif /* * update claim counters */ sys->claims = statesIncrease (sys->claims); /* * distinguish claim types */ if (runPoint->to == CLAIM_Secret) { /* * SECRECY */ /* TODO claims now have their own type, test for that */ /* TODO for now it is secrecy of the message */ Termlist oldsecrets = sys->secrets; /* TODO this can be more efficient, by filtering out double occurrences */ sys->secrets = termlistAdd (termlistShallow (oldsecrets), runPoint->message); flag = claimSecrecy (sys, runPoint->message); runPoint->claiminfo->count++; /* now check whether the claim failed for further actions */ if (!flag) { /* violation */ Termlist tl; tl = claimViolationDetails (sys, run, runPoint, sys->know); if (violateClaim (sys, sys->step + 1, sys->step, tl)) flag = explorify (sys, run); termlistDelete (tl); } else { /* no violation */ flag = explorify (sys, run); } /* reset secrets list */ termlistDelete (sys->secrets); sys->secrets = oldsecrets; } else if (runPoint->to == CLAIM_Nisynch) { /* * NISYNCH */ flag = check_claim_nisynch (sys, sys->step); if (!flag) { /* violation */ if (violateClaim (sys, sys->step + 1, sys->step, NULL)) flag = explorify (sys, run); } else { /* no violation */ flag = explorify (sys, run); } } else if (runPoint->to == CLAIM_Niagree) { /* * NIAGREE */ flag = check_claim_niagree (sys, sys->step); if (!flag) { /* violation */ if (violateClaim (sys, sys->step + 1, sys->step, NULL)) flag = explorify (sys, run); } else { /* no violation */ flag = explorify (sys, run); } } else // if (runPoint->to == CLAIM_Empty) { // Skip other claim types flag = explorify (sys, run); } } /* a claim always succeeds */ flag = 1; } return flag; }