/* * Scyther : An automatic verifier for security protocols. * Copyright (C) 2007-2013 Cas Cremers * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "system.h" #include "switches.h" #include "arachne.h" #include "binding.h" #include "depend.h" #include "type.h" #include "debug.h" #include "error.h" #include "specialterm.h" #include "cost.h" extern Protocol INTRUDER; // Pointers, to be set by the Init of arachne.c extern Role I_M; // Same here. extern Role I_RRS; extern Role I_RRSD; #define INVALID -1 #define isGoal(rd) (rd->type == RECV && !rd->internal) #define isBound(rd) (rd->bound) #define length step #define CLAIMTEXTCOLOR "#ffffff" #define CLAIMCOLOR "#000000" #define GOODCOMMCOLOR "forestgreen" #define INTRUDERCOLORH 18.0 #define INTRUDERCOLORL 0.65 #define INTRUDERCOLORS 0.9 #define RUNCOLORL1 0.90 #define RUNCOLORL2 0.65 #define RUNCOLORH1 (INTRUDERCOLORH + 360 - 10.0) #define RUNCOLORH2 (INTRUDERCOLORH + 10.0) #define RUNCOLORS1 0.8 #define RUNCOLORS2 0.6 #define RUNCOLORDELTA 0.2 // maximum hue delta between roles (0.2): smaller means role colors of a protocol become more similar. #define RUNCOLORCONTRACT 0.8 // contract from protocol edges: smaller means more distinction between protocols. #define UNTRUSTEDCOLORS 0.4 #define CHOOSEWEIGHT "2.0" #define RUNWEIGHT "10.0" //#define CHOOSEWEIGHT "1.0" //#define RUNWEIGHT "1.0" /* * Dot output * * * The algorithm itself is not very complicated; because the semi-bundles have * bindings etcetera, a graph can be draw quickly and efficiently. * * Interesting issues: * * Binding annotations are only drawn if they don't connect with regular * events, and when the item does not occur in any previous binding, it might * be connected to the initial intruder knowledge. * * Color management is quite involved. We draw identical protocols in similar * color schemes. A color scheme is a gradient between two colors, evenly * spread over all the runs. */ static System sys = NULL; /* * code */ //! Is this term chosen by the intruder? int isIntruderChoice (const Term t) { if (realTermLeaf (t)) { if (TermRunid (t) >= sys->maxruns) { // Chosen by intruder // However, if it is a rolename, this is not really what we mean if (!(t->helper.roleVar || isAgentType (t->stype))) { // Not a role variable, and chosen by the intruder: that's it return true; } } } return false; } //! Print the run identifier in some meaningful way void printVisualRunID (int rid) { int run; int displayi; int displayr; int display; if (rid < sys->maxruns) { // < sys->maxruns means normal thing (not from makeTraceConcrete) displayi = 0; displayr = 0; for (run = 0; run < rid; run++) { if (sys->runs[run].protocol != INTRUDER) { displayr++; } else { displayi++; } } if (sys->runs[rid].protocol == INTRUDER) { display = sys->maxruns + displayi + 1; } else { display = displayr + 1; } eprintf ("#%i", display); } else { eprintf ("%i", (rid - sys->maxruns + 1)); } } void printVisualRun (const Term t) { if (isIntruderChoice (t)) { eprintf ("Intruder"); } printVisualRunID (TermRunid (t)); } //! Remap term stuff void termPrintRemap (const Term t) { termPrintCustom (t, "", "", "(", ")", "\\{ ", " \\}", printVisualRun); } //! Remap term list void termlistPrintRemap (Termlist tl, char *sep) { while (tl != NULL) { termPrintRemap (tl->term); tl = tl->next; if (tl != NULL) { eprintf ("%s", sep); } } } //! Print a term; if it is a variable, show that void explainVariable (Term t) { t = deVar (t); if (realTermVariable (t)) { eprintf ("any "); if (t->helper.roleVar) { eprintf ("agent "); } termPrintRemap (t); if (!t->helper.roleVar) { if (switches.match == 0 && t->stype != NULL) { Termlist tl; eprintf (" of type "); for (tl = t->stype; tl != NULL; tl = tl->next) { termPrintRemap (tl->term); if (tl->next != NULL) { eprintf (","); } } } } } else { termPrintRemap (t); } } //! Name of intruder node void intruderNodeM0 (void) { eprintf ("intruder"); } //! Draw node void node (const System sys, const int run, const int index) { if (sys->runs[run].protocol == INTRUDER) { if (sys->runs[run].role == I_M) { intruderNodeM0 (); } else { eprintf ("ri%i", run); } } else { eprintf ("r%ii%i", run, index); } } //! Draw arrow void arrow (const System sys, Binding b) { node (sys, b->run_from, b->ev_from); eprintf (" -> "); node (sys, b->run_to, b->ev_to); } //! Redirect node void redirNode (const System sys, Binding b) { eprintf ("redir_"); node (sys, b->run_from, b->ev_from); node (sys, b->run_to, b->ev_to); } //! Roledef draw void roledefDraw (Roledef rd) { void optlabel (void) { Term label; label = rd->label; if (label != NULL) { if (realTermTuple (label)) { label = TermOp2 (label); } eprintf ("_"); termPrintRemap (label); } } if (rd->type == RECV) { eprintf ("recv"); optlabel (); eprintf (" from "); termPrintRemap (rd->from); eprintf ("\\n"); termPrintRemap (rd->message); } if (rd->type == SEND) { eprintf ("send"); optlabel (); eprintf (" to "); termPrintRemap (rd->to); eprintf ("\\n"); termPrintRemap (rd->message); } if (rd->type == CLAIM) { eprintf ("claim"); optlabel (); eprintf ("\\n"); termPrintRemap (rd->to); if (rd->message != NULL) { eprintf (" : "); termPrintRemap (rd->message); } } } //! Choose term node void chooseTermNode (const Term t) { eprintf ("CHOOSE"); { char *rsbuf; rsbuf = RUNSEP; RUNSEP = "x"; termPrint (t); RUNSEP = rsbuf; } } //! Value for hlsrgb conversion static double hlsValue (double n1, double n2, double hue) { if (hue > 360.0) hue -= 360.0; else if (hue < 0.0) hue += 360.0; if (hue < 60.0) return n1 + (n2 - n1) * hue / 60.0; else if (hue < 180.0) return n2; else if (hue < 240.0) return n1 + (n2 - n1) * (240.0 - hue) / 60.0; else return n1; } //! hls to rgb conversion void hlsrgbreal (int *r, int *g, int *b, double h, double l, double s) { double m1, m2; int bytedouble (double d) { double x; x = 255.0 * d; if (x <= 0) return 0; else if (x >= 255.0) return 255; else return (int) x; } while (h >= 360.0) h -= 360.0; while (h < 0) h += 360.0; m2 = (l <= 0.5) ? (l * (l + s)) : (l + s - l * s); m1 = 2.0 * l - m2; if (s == 0.0) { *r = *g = *b = bytedouble (l); } else { *r = bytedouble (hlsValue (m1, m2, h + 120.0)); *g = bytedouble (hlsValue (m1, m2, h)); *b = bytedouble (hlsValue (m1, m2, h - 120.0)); } } //! hls to rgb conversion /** * Secretly takes the monochrome switch into account */ void hlsrgb (int *r, int *g, int *b, double h, double l, double s) { double closer (double l, double factor) { return l + ((1.0 - l) * factor); } if (switches.monochrome) { // No colors s = 0; h = 0; } if (switches.lightness > 0) { // correction switch for lightness if (switches.lightness == 100) { l = 1.0; } else { l = closer (l, ((double) switches.lightness / 100.0)); } } // convert hlsrgbreal (r, g, b, h, l, s); } //! print color from h,l,s triplet void printColor (double h, double l, double s) { int r, g, b; hlsrgb (&r, &g, &b, h, l, s); eprintf ("#%02x%02x%02x", r, g, b); } //! Set local buffer with the correct color for this run. /** * Determines number of protocols, shifts to the right color pair, and colors * the run within the current protocol in the fade between the color pair. * * This can be done much more efficiently by computing these colors once, * instead of each time again for each run. However, this is not a * speed-critical section so this will do just nicely. */ void setRunColorBuf (const System sys, int run, char *colorbuf) { int range; int index; double protoffset, protrange; double roleoffset, roledelta; double color; double h, l, s; int r, g, b; // help function: contract roleoffset, roledelta with a factor (<= 1.0) void contract (double factor) { roledelta = roledelta * factor; roleoffset = (roleoffset * factor) + ((1.0 - factor) / 2.0); } // determine #protocol, resulting in two colors { Termlist protocols; Term refprot; int r; int firstfound; protocols = NULL; refprot = sys->runs[run].protocol->nameterm; index = 0; range = 1; firstfound = false; for (r = 0; r < sys->maxruns; r++) { if (sys->runs[r].protocol != INTRUDER) { Term prot; prot = sys->runs[r].protocol->nameterm; if (!isTermEqual (prot, refprot)) { // Some 'other' protocol if (!inTermlist (protocols, prot)) { // New other protocol protocols = termlistAdd (protocols, prot); range++; if (!firstfound) { index++; } } } else { // Our protocol firstfound = true; } } } termlistDelete (protocols); } // Compute protocol offset [0.0 ... 1.0> protrange = 1.0 / range; protoffset = index * protrange; // We now now our range, and we can determine which role this one is. { Role rr; int done; range = 0; index = 0; done = false; for (rr = sys->runs[run].protocol->roles; rr != NULL; rr = rr->next) { if (sys->runs[run].role == rr) { done = true; } else { if (!done) { index++; } } range++; } } // Compute role offset [0.0 ... 1.0] if (range <= 1) { roledelta = 0.0; roleoffset = 0.5; } else { // range over 0..1 roledelta = 1.0 / (range - 1); roleoffset = index * roledelta; // Now this can result in a delta that is too high (depending on protocolrange) if (protrange * roledelta > RUNCOLORDELTA) { contract (RUNCOLORDELTA / (protrange * roledelta)); } } // We slightly contract the colors (taking them away from protocol edges) contract (RUNCOLORCONTRACT); // Now we can convert this to a color color = protoffset + (protrange * roleoffset); h = RUNCOLORH1 + color * (RUNCOLORH2 - RUNCOLORH1); l = RUNCOLORL1 + color * (RUNCOLORL2 - RUNCOLORL1); s = RUNCOLORS1 + color * (RUNCOLORS2 - RUNCOLORS1); // If the run is not trusted, we lower the saturation significantly if (!isRunTrusted (sys, run)) { s = UNTRUSTEDCOLORS; } // set to buffer hlsrgb (&r, &g, &b, h, l, s); sprintf (colorbuf, "#%02x%02x%02x", r, g, b); // compute second color (light version) /* l += 0.07; if (l > 1.0) l = 1.0; */ hlsrgb (&r, &g, &b, h, l, s); sprintf (colorbuf + 8, "#%02x%02x%02x", r, g, b); } //! Communication status int isCommunicationExact (const System sys, Binding b) { Roledef rd1, rd2; rd1 = eventRoledef (sys, b->run_from, b->ev_from); rd2 = eventRoledef (sys, b->run_to, b->ev_to); if (!isTermEqual (rd1->message, rd2->message)) { return false; } if (!isTermEqual (rd1->from, rd2->from)) { return false; } if (!isTermEqual (rd1->to, rd2->to)) { return false; } if (!isTermEqual (rd1->label, rd2->label)) { return false; } return true; } //! Ignore some events /** * Used only in graph drawing. Return true to ignore, return false to * draw. */ int isEventIgnored (const System sys, int run, int ev) { Roledef rd; rd = eventRoledef (sys, run, ev); if (rd->type == CLAIM) { // If we are doing a reachability analysis, show all if (isTermEqual (sys->current_claim->type, CLAIM_Reachable)) { return false; } // If the claim type is commit, we also show running claims if (isTermEqual (sys->current_claim->type, CLAIM_Commit) || isTermEqual (sys->current_claim->type, CLAIM_Reachable)) { if (isTermEqual (rd->claiminfo->type, CLAIM_Running)) { return false; } } // Remaining cases: only active claim is shown if (run == 0) { if (ev == sys->current_claim->ev) { return false; } } return true; } return false; } //! Check whether an event is a function application int isApplication (const System sys, const int run) { if (sys->runs[run].protocol == INTRUDER) { if (sys->runs[run].role == I_RRS) { Roledef rd; rd = sys->runs[run].start->next; if (rd != NULL) { if (isTermFunctionName (rd->message)) { return true; } } } } return false; } //! Is an event enabled by M0 only? int isEnabledM0 (const System sys, const int run, const int ev) { List bl; for (bl = sys->bindings; bl != NULL; bl = bl->next) { Binding b; b = (Binding) bl->data; if (!b->blocked) { // if the binding is not done (class choice) we might // still show it somewhere. if (b->done) { if (b->run_to == run && b->ev_to == ev) { if (sys->runs[b->run_from].role != I_M) { return false; } } } } } return true; } //! Check whether the event is an M_0 function application (special case of the previous) int isApplicationM0 (const System sys, const int run) { if (sys->runs[run].length > 1) { if (isApplication (sys, run)) { if (isEnabledM0 (sys, run, 0)) { if (isEnabledM0 (sys, run, 1)) { return true; } } } } return false; } //! Helper for graph_ranks /** * Name & documentation might be off; TODO later. * This is for now just a refactoring to get rid of trampolines. */ int preceventPossible (const System sys, const int rank, const int run, const int rank2, const int run2, const int ev2) { // regular preceding event if (rank2 > rank) { // higher rank, this cannot be done return false; } if (rank2 == rank) { // equal rank: only if different run if ((sys->runs[run].protocol != INTRUDER) && (run2 == run)) { return false; } } return true; } //! Helper for graph_ranks /** * Name & documentation might be off; TODO later. * This is for now just a refactoring to get rid of trampolines. */ int iteratePrecedingRole (const System sys, const int *ranks, const int run, const int ev, const int rank) { int run2; for (run2 = 0; run2 < sys->maxruns; run2++) { int ev2; for (ev2 = 0; ev2 < sys->runs[run2].step; ev2++) { if (isDependEvent (run2, ev2, run, ev)) { int rank2; rank2 = ranks[eventNode (run2, ev2)]; if (!preceventPossible (sys, rank, run, rank2, run2, ev2)) { return false; } } } } return true; } //! Determine ranks for all nodes /** * Some crude algorithm I sketched on the blackboard. */ int graph_ranks (int *ranks, int nodes) { int done; int rank; int changes; int i; #ifdef DEBUG if (hasCycle ()) { error ("Graph ranks tried, but a cycle exists!"); } #endif for (i = 0; i < nodes; i++) { ranks[i] = INT_MAX; } rank = 0; done = false; changes = true; while (!done) { int run; if (!changes) { rank++; if (rank >= nodes) { warning ("Rank %i increased to the number of nodes %i.", rank, nodes); return rank; } } done = true; changes = false; for (run = 0; run < sys->maxruns; run++) { Roledef rd; int ev; rd = sys->runs[run].start; for (ev = 0; ev < sys->runs[run].step; ev++) { if (rd != NULL) // Shouldn't be needed (step should maintain invariant) but good to be safe { if (ranks[eventNode (run, ev)] == INT_MAX) { if (iteratePrecedingRole (sys, ranks, run, ev, rank)) { // we can do it! changes = true; ranks[eventNode (run, ev)] = rank; } else { done = false; } } rd = rd->next; } } } } return rank; } //! Display the ranks /** * Reinstated after it had been gone for a while */ void showRanks (const System sys, const int maxrank, const int *ranks, const int nodes) { int rank; //return; for (rank = 0; rank <= maxrank; rank++) { int found; int run; found = 0; for (run = 0; run < sys->maxruns; run++) { if (sys->runs[run].protocol != INTRUDER) { int ev; for (ev = 0; ev < sys->runs[run].step; ev++) { if (!isEventIgnored (sys, run, ev)) { int n; n = eventNode (run, ev); if (ranks[n] == rank) { if (found == 0) { eprintf ("\t{ rank = same; "); } node (sys, run, ev); eprintf ("; "); found++; } } } } } if (found > 0) { eprintf ("}\n"); } } } //! Does a term occur in a run? int termOccursInRun (Term t, int run) { Roledef rd; int e; rd = sys->runs[run].start; e = 0; while (e < sys->runs[run].step) { if (roledefSubTerm (rd, t)) { return true; } e++; rd = rd->next; } return false; } //! Draw a class choice /** * \rho classes are already dealt with in the headers, so we should ignore them. */ void drawClass (const System sys, Binding b) { Term varterm; // now check in previous things whether we saw that term already int notSameTerm (Binding b2) { return (!isTermEqual (varterm, b2->term)); } varterm = deVar (b->term); // Variable? if (!isTermVariable (varterm)) { return; } // Agent variable? { int run; run = TermRunid (varterm); if ((run >= 0) && (run < sys->maxruns)) { if (inTermlist (sys->runs[run].rho, varterm)) { return; } } } // Seen before? if (!iterate_preceding_bindings (b->run_to, b->ev_to, notSameTerm)) { // We saw the same term before. Exit. return; } // not seen before: choose class eprintf ("\t"); chooseTermNode (varterm); eprintf (" [label=\""); explainVariable (varterm); eprintf ("\"];\n"); eprintf ("\t"); chooseTermNode (varterm); eprintf (" -> "); node (sys, b->run_to, b->ev_to); eprintf (" [weight=\"%s\",arrowhead=\"none\",style=\"dotted\"];\n", CHOOSEWEIGHT); } //! Print label of a regular->regular transition node (when comm. is not exact) /** * Note that we ignore any label differences, these are left implicit */ void regularModifiedLabel (Binding b) { Roledef rdfrom; Roledef rdto; int unknown; rdfrom = eventRoledef (sys, b->run_from, b->ev_from); rdto = eventRoledef (sys, b->run_to, b->ev_to); unknown = true; // First up: compare messages contents': what was sent, what is needed if (!isTermEqual (rdfrom->message, b->term)) { // What is sent is not equal to what is bound if (termInTerm (rdfrom->message, b->term)) { // Interm: simple select unknown = false; eprintf ("select "); termPrintRemap (b->term); eprintf ("\\n"); } } // Second: agent things if (!isTermEqual (rdfrom->from, rdto->from)) { unknown = false; eprintf ("fake sender "); termPrintRemap (rdto->from); eprintf ("\\n"); } if (!isTermEqual (rdfrom->to, rdto->to)) { unknown = false; eprintf ("redirect to "); termPrintRemap (rdto->to); eprintf ("\\n"); } // Any leftovers for which I don't have a good name yet. if (unknown) { // I'm not quite sure, we call it 'combine' for now. TODO eprintf ("combine\\n"); } } //! Draw a single binding void drawBinding (const System sys, Binding b) { int intr_to, intr_from, m0_from; void myarrow (const Binding b) { if (m0_from) { eprintf ("\t"); intruderNodeM0 (); eprintf (" -> "); node (sys, b->run_to, b->ev_to); } else { arrow (sys, b); } } intr_from = (sys->runs[b->run_from].protocol == INTRUDER); intr_to = (sys->runs[b->run_to].protocol == INTRUDER); m0_from = false; // Pruning: things going to M0 applications are pruned; if (isApplicationM0 (sys, b->run_to)) { return; } if (isApplicationM0 (sys, b->run_from) || sys->runs[b->run_from].role == I_M) { m0_from = true; } // Normal drawing cases; if (intr_from) { // from intruder /* * Because this can be generated many times, it seems * reasonable to not duplicate such arrows, especially when * they're from M_0. Maybe the others are still relevant. */ if (1 == 1 || sys->runs[b->run_from].role == I_M) { // now check in previous things whether we saw that term already int notSameTerm (Binding b2) { return (!isTermEqual (b->term, b2->term)); } if (!iterate_preceding_bindings (b->run_to, b->ev_to, notSameTerm)) { // We saw the same term before. Exit. return; } } // normal from intruder, not seen before (might be M_0) if (intr_to) { // intr->intr eprintf ("\t"); myarrow (b); eprintf (" [label=\""); termPrintRemap (b->term); eprintf ("\""); if (m0_from) { eprintf (",weight=\"10.0\""); } eprintf ("]"); eprintf (";\n"); } else { // intr->regular eprintf ("\t"); myarrow (b); if (m0_from) { eprintf ("[weight=\"0.5\"]"); } eprintf (";\n"); } } else { // not from intruder if (intr_to) { // regular->intr eprintf ("\t"); myarrow (b); eprintf (";\n"); } else { // regular->regular /* * Has this been done *exactly* as we hoped? */ if (isCommunicationExact (sys, b)) { eprintf ("\t"); myarrow (b); eprintf (" [style=bold,color=\"%s\"]", GOODCOMMCOLOR); eprintf (";\n"); } else { // Something was changed, so we call this a redirect eprintf ("\t"); node (sys, b->run_from, b->ev_from); eprintf (" -> "); redirNode (sys, b); eprintf (" -> "); node (sys, b->run_to, b->ev_to); eprintf (";\n"); eprintf ("\t"); redirNode (sys, b); eprintf (" [style=filled,fillcolor=\""); printColor (INTRUDERCOLORH, INTRUDERCOLORL, INTRUDERCOLORS); eprintf ("\",label=\""); regularModifiedLabel (b); eprintf ("\"]"); eprintf (";\n"); } } } } //! Draw dependecies (including intruder!) /** * Returns from_intruder_count (from M_0) */ int drawAllBindings (const System sys) { List bl; List bldone; int fromintr; bldone = NULL; fromintr = 0; for (bl = sys->bindings; bl != NULL; bl = bl->next) { Binding b; b = (Binding) bl->data; if (!b->blocked) { // if the binding is not done (class choice) we might // still show it somewhere. if (b->done) { // Check whether we already drew it List bl2; int drawn; drawn = false; for (bl2 = bldone; bl2 != NULL; bl2 = bl2->next) { if (same_binding (b, (Binding) bl2->data)) { drawn = true; break; } } if (!drawn) { // done, draw drawBinding (sys, b); // from intruder? if (sys->runs[b->run_from].protocol == INTRUDER) { if (sys->runs[b->run_from].role == I_M) { fromintr++; } } // Add to drawn list bldone = list_add (bldone, b); } } else { drawClass (sys, b); } } } list_destroy (bldone); // bindings list return fromintr; } //! Print "Alice in role R" of a run void printAgentInRole (const System sys, const int run) { Term rolename; Term agentname; rolename = sys->runs[run].role->nameterm; agentname = agentOfRunRole (sys, run, rolename); explainVariable (agentname); eprintf (" in role "); termPrintRemap (rolename); } //! rho, sigma, const /* * true if it has printed */ int showLocal (const int run, Term told, Term tnew, char *prefix, char *cursep) { if (realTermVariable (tnew)) { if (termOccursInRun (tnew, run)) { // Variables are mapped, maybe. But then we wonder whether they occur in recvs. eprintf (cursep); eprintf (prefix); termPrintRemap (told); eprintf (" -\\> "); explainVariable (tnew); } else { return false; } } else { eprintf (cursep); eprintf (prefix); termPrintRemap (tnew); } return true; } //! show a list of locals /** * never ends with the seperator */ int showLocals (const int run, Termlist tlold, Termlist tlnew, Term tavoid, char *prefix, char *sep) { int anything; char *cursep; cursep = ""; anything = false; while (tlold != NULL && tlnew != NULL) { if (!isTermEqual (tlold->term, tavoid)) { if (showLocal (run, tlold->term, tlnew->term, prefix, cursep)) { cursep = sep; anything = true; } } tlold = tlold->next; tlnew = tlnew->next; } return anything; } //! Explain the local constants /** * Return true iff something was printed */ int printRunConstants (const System sys, const int run) { if (sys->runs[run].constants != NULL) { eprintf ("Fresh "); showLocals (run, sys->runs[run].role->declaredconsts, sys->runs[run].constants, NULL, "", ", "); eprintf ("\\l"); return true; } else { return false; } } //! Explain a run in two lines void printRunExplanation (const System sys, const int run, char *runrolesep, char *newline) { int hadcontent; eprintf ("Run "); printVisualRunID (run); eprintf (runrolesep); // Print first line printAgentInRole (sys, run); eprintf ("\\l"); // Second line // Possible protocol (if more than one) { int showprotocol; Protocol p; int morethanone; // Simple case: don't show showprotocol = false; // Check whether the protocol spec has more than one morethanone = false; for (p = sys->protocols; p != NULL; p = p->next) { if (p != INTRUDER) { if (p != sys->runs[run].protocol) { morethanone = true; break; } } } // More than one? if (morethanone) { // This used to work for run 0 always... //if (run == 0) if (false) { // If this is run 0 we report the protocol anyway, even is there is only a single one in the attack showprotocol = true; } else { int r; // For other runs we only report when there are multiple protocols showprotocol = false; for (r = 0; r < sys->maxruns; r++) { if (sys->runs[r].protocol != INTRUDER) { if (sys->runs[r].protocol != sys->runs[run].protocol) { showprotocol = true; break; } } } } } // Use the result if (showprotocol) { eprintf ("Protocol "); termPrintRemap (sys->runs[run].protocol->nameterm); eprintf ("\\l"); } } eprintf (newline); hadcontent = false; { /* * Originally, we ignored the actor in the rho list, but for more than two-party protocols, this was unclear. */ int numroles; int ignoreactor; ignoreactor = false; // set to true to ignore the actor numroles = termlistLength (sys->runs[run].rho); if (numroles > 1) { { Term ignoreterm; if (ignoreactor) { ignoreterm = sys->runs[run].role->nameterm; } else { ignoreterm = NULL; } hadcontent = showLocals (run, sys->runs[run].protocol->rolenames, sys->runs[run].rho, ignoreterm, "", "\\l"); } } } if (hadcontent) { eprintf ("\\l"); eprintf (newline); hadcontent = false; } hadcontent = printRunConstants (sys, run); if (sys->runs[run].sigma != NULL) { if (hadcontent) { eprintf (newline); hadcontent = false; } if (showLocals (run, sys->runs[run].role->declaredvars, sys->runs[run].sigma, NULL, "Var ", "\\l")) { eprintf ("\\l"); } } } //! Draw regular runs void drawRegularRuns (const System sys) { int run; char *colorbuf; // two buffers, eight chars each colorbuf = malloc (16 * sizeof (char)); for (run = 0; run < sys->maxruns; run++) { if (sys->runs[run].length > 0) { if (sys->runs[run].protocol != INTRUDER) { Roledef rd; int index; int prevnode; int firstnode; rd = sys->runs[run].start; // Regular run if (switches.clusters) { eprintf ("\tsubgraph cluster_run%i {\n", run); eprintf ("\t\tstyle=filled;\n"); eprintf ("\t\tcolor=lightgrey;\n"); eprintf ("\t\tlabel=\""); printRunExplanation (sys, run, " : ", ""); eprintf ("\";\n\n"); } // set color setRunColorBuf (sys, run, colorbuf); // Display the respective events prevnode = 0; firstnode = true; for (index = 0; index < sys->runs[run].length; index++) { /* * invariant: prevnode has been drawn OR firstnode is true */ if (!isEventIgnored (sys, run, index)) { // Print node itself eprintf ("\t\t"); node (sys, run, index); eprintf (" ["); if (run == 0 && index == sys->current_claim->ev) { // The claim under scrutiny eprintf ("style=filled,fontcolor=\"%s\",fillcolor=\"%s\",shape=box,", CLAIMTEXTCOLOR, CLAIMCOLOR); } else { eprintf ("shape=box,style=filled,"); // print color of this run eprintf ("fillcolor=\"%s\",", colorbuf); } eprintf ("label=\""); //roledefPrintShort (rd); roledefDraw (rd); eprintf ("\"]"); eprintf (";\n"); // Print binding to previous node if (firstnode == false) { // not the first node eprintf ("\t\t"); node (sys, run, prevnode); eprintf (" -> "); node (sys, run, index); eprintf (" [style=\"bold\", weight=\"%s\"]", RUNWEIGHT); eprintf (";\n"); } else { // firstnode if (!switches.clusters) { // Draw the first box (HEADER) eprintf ("\t\ts%i [label=\"{ ", run); printRunExplanation (sys, run, "\\l", "|"); // close up eprintf ("}\", shape=record"); eprintf (",style=filled,fillcolor=\"%s\"", colorbuf + 8); eprintf ("];\n"); eprintf ("\t\ts%i -> ", run); node (sys, run, index); eprintf (" [style=bold, weight=\"%s\"];\n", RUNWEIGHT); } firstnode = false; } prevnode = index; } rd = rd->next; } if (switches.clusters) { eprintf ("\t}\n"); } } } } free (colorbuf); } //! Draw intruder runs void drawIntruderRuns (const System sys) { int run; if (switches.clusters) { //eprintf ("\tsubgraph cluster_intruder {\n"); eprintf ("\tsubgraph intr {\n"); eprintf ("\t\tlabel = \"Intruder\";\n"); eprintf ("\t\tcolor = red;\n"); } for (run = 0; run < sys->maxruns; run++) { if (sys->runs[run].length > 0) { if (sys->runs[run].protocol == INTRUDER) { // Intruder run if (sys->runs[run].role != I_M && !isApplicationM0 (sys, run)) { // Not an M_0 run, and not an M0 function application, so we can draw it. eprintf ("\t\t"); node (sys, run, 0); eprintf (" [style=filled,fillcolor=\""); printColor (INTRUDERCOLORH, INTRUDERCOLORL, INTRUDERCOLORS); eprintf ("\","); if (sys->runs[run].role == I_RRSD) { eprintf ("label=\"decrypt\""); } if (sys->runs[run].role == I_RRS) { // Distinguish function application if (isTermFunctionName (sys->runs[run].start->next->message)) { eprintf ("label=\"apply\""); } else { eprintf ("label=\"encrypt\""); } } eprintf ("];\n"); } } } } if (switches.clusters) { eprintf ("\t}\n\n"); } } //! Display the current semistate using dot output format. /** * This is not as nice as we would like it. Furthermore, the function is too big. */ void dotSemiState (const System mysys) { static int attack_number = 0; Protocol p; int *ranks; int maxrank; int from_intruder_count; int nodes; sys = mysys; // Open graph attack_number++; eprintf ("digraph semiState%i {\n", attack_number); eprintf ("\tlabel = \"[Id %i] Protocol ", sys->attackid); p = (Protocol) sys->current_claim->protocol; termPrintRemap (p->nameterm); eprintf (", role "); termPrintRemap (sys->current_claim->rolename); eprintf (", claim type "); termPrintRemap (sys->current_claim->type); // For debugging: //eprintf (", cost %i", computeAttackCost (sys)); eprintf ("\";\n"); // Needed for the bindings later on: create graph nodes = nodeCount (); ranks = malloc (nodes * sizeof (int)); maxrank = graph_ranks (ranks, nodes); // determine ranks #ifdef DEBUG if (DEBUGL (1)) { // For debugging purposes, we also display an ASCII version of some stuff in the comments printSemiState (); // Even draw all dependencies for non-intruder runs // Real nice debugging :( int run; run = 0; while (run < sys->maxruns) { int ev; ev = 0; while (ev < sys->runs[run].length) { int run2; int notfirstrun; eprintf ("// precedence: r%ii%i <- ", run, ev); run2 = 0; notfirstrun = 0; while (run2 < sys->maxruns) { int notfirstev; int ev2; notfirstev = 0; ev2 = 0; while (ev2 < sys->runs[run2].length) { if (isDependEvent (run2, ev2, run, ev)) { if (notfirstev) eprintf (","); else { if (notfirstrun) eprintf (" "); eprintf ("r%i:", run2); } eprintf ("%i", ev2); notfirstrun = 1; notfirstev = 1; } ev2++; } run2++; } eprintf ("\n"); ev++; } run++; } } #endif // First, runs drawRegularRuns (sys); drawIntruderRuns (sys); from_intruder_count = drawAllBindings (sys); // Third, the intruder node (if needed) { /* * Stupid brute analysis, can probably be done much more efficient, but * this is not a timing critical bit, so we just do it like this. */ Termlist found; List bl; // collect the intruder-generated constants found = NULL; for (bl = sys->bindings; bl != NULL; bl = bl->next) { Binding b; b = (Binding) bl->data; if (!b->blocked) { int addsubterms (Term t) { if (isIntruderChoice (t)) { found = termlistAddNew (found, t); } return true; } term_iterate_open_leaves (b->term, addsubterms); } } // now maybe we draw the node if ((from_intruder_count > 0) || (found != NULL)) { eprintf ("\tintruder [\n"); eprintf ("\t\tlabel=\""); eprintf ("Initial intruder knowledge"); if (found != NULL) { eprintf ("\\n"); eprintf ("The intruder generates: "); termlistPrintRemap (found, ", "); } eprintf ("\",\n"); eprintf ("\t\tstyle=filled,fillcolor=\""); printColor (INTRUDERCOLORH, INTRUDERCOLORL, INTRUDERCOLORS); eprintf ("\"\n\t];\n"); } termlistDelete (found); } // eprintf ("\t};\n"); // For debugging we might add more stuff: full dependencies #ifdef DEBUG if (DEBUGL (3)) { int r1; for (r1 = 0; r1 < sys->maxruns; r1++) { if (sys->runs[r1].protocol != INTRUDER) { int e1; for (e1 = 0; e1 < sys->runs[r1].step; e1++) { int r2; for (r2 = 0; r2 < sys->maxruns; r2++) { if (sys->runs[r2].protocol != INTRUDER) { int e2; for (e2 = 0; e2 < sys->runs[r2].step; e2++) { if (isDependEvent (r1, e1, r2, e2)) { eprintf ("\tr%ii%i -> r%ii%i [color=grey];\n", r1, e1, r2, e2); } } } } } } } } #endif // Ranks if (switches.clusters) { showRanks (sys, maxrank, ranks, nodes); } #ifdef DEBUG // Debug: print dependencies if (DEBUGL (3)) { dependPrint (); } #endif // clean memory free (ranks); // ranks // close graph eprintf ("}\n\n"); }