diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..48ae1d4 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,80 @@ +# +# Scyther Makefile +# + +# +# DEBUG or optimization settings: uncomment a single line: +# +CFLAGS = -g3 -D DEBUG # default usage, for e.g. with valgrind +#CFLAGS = -g3 -D DEBUG -pg # for code profiling with gprof +#CFLAGS = -O3 -static -finline-functions -fomit-frame-pointer + +# +# Compiler and linkage +# +CC = gcc +# Note that these paths must include the path to the argtable library. +CPPFLAGS = -I/scratch/ccremers/include -I/usr/local/include -Wall +LDFLAGS = -L/scratch/ccremers/lib -L/usr/local/lib +LOADLIBS = -lfl +LDLIBS = -largtable2 +OPTIONS = ${CPPFLAGS} ${CFLAGS} ${LDFLAGS} + +# +# Module set for the modelchecker +# +MODULES=memory.o terms.o termlists.o symbols.o knowledge.o runs.o modelchecker.o \ +report.o debug.o mgu.o substitutions.o \ +match_basic.o \ +match_clp.o constraints.o \ +output.o latex.o \ +varbuf.o tracebuf.o attackminimize.o \ +tac.o parser.o compiler.o + +# +# Dependencies +# +MODELCHECKER = ${MODULES} main.o + +all: scyther tags + +${Target}.o: ${Target}.c + $(CC) $(OPTIONS) ${Target}.c -c + +scanner.c: scanner.lex + flex scanner.lex + cp lex.yy.c scanner.c + +tok.h: parser.c + +parser.c: parser.y + bison -d -v parser.y + cp parser.tab.c parser.c + cmp -s parser.tab.h tok.h || cp parser.tab.h tok.h + +tags: *.c *.h + ctags *.c *.h + +modules: $(MODULES) + +scyther: scanner.o $(MODELCHECKER) + $(CC) $(OPTIONS) $(MODELCHECKER) -o scyther $(LOADLIBS) $(LDLIBS) + +ptestmain.o scanner.o : tok.h + +# +# Cleanup +# +clean: + rm -f *.o + rm -f scyther + rm -f scanner.c + rm -f parser.c + rm -f tok.h +# +# Clean and rebuild: 'make new' +# +new: + make clean + make all + diff --git a/src/README b/src/README new file mode 100644 index 0000000..bd00924 --- /dev/null +++ b/src/README @@ -0,0 +1,33 @@ +================== +# Scyther README # +================== + +---------------- +1. About Scyther +---------------- + +Scyther bla Cas andsoforth, bla Lutger, yada yada yada. + +--------------- +2. Requirements +--------------- + +Scyther compilation depends on a few external items: + +- A C compiler (gcc or anything modern *nixy will do) +- The Flex and Bison scanner/compiler generation tools. +- The argtable 2 library. + +The first two requirements are usually met by default by any modern *nix +variant, such as GNU/Linux. + +The argtable2 library is available under the LGPL license from +http://argtable.sourceforge.net/doc/argtable2.html. Make sure the paths +in the Makefile include the path to this library. + +If you want LaTeX output we need + +- LaTeX +- The MSC macro package msc.sty +- preamble.tex and postamble.tex + diff --git a/src/attackminimize.c b/src/attackminimize.c new file mode 100644 index 0000000..220dc32 --- /dev/null +++ b/src/attackminimize.c @@ -0,0 +1,131 @@ +#include +#include +#include "runs.h" +#include "tracebuf.h" + +int cUnk = 0; +int cTod = 0; + +void markback(System sys, struct tracebuf *tb, int ev) +{ + int run = tb->run[ev]; + + while (ev >= 0) + { + if (tb->run[ev] == run) + { + switch (tb->event[ev]->type) + { + case READ: + switch (tb->status[ev]) + { + case S_UNK: + cUnk--; + case S_RED: + tb->status[ev] = S_TOD; + cTod++; + break; + case S_TOD: + case S_OKE: + break; + } + break; + case SEND: + case CLAIM: + if (tb->status[ev] == S_UNK) + { + cUnk--; + } + tb->status[ev] = S_OKE; + break; + } + } + ev--; + } +} + +void attackMinimize(System sys, struct tracebuf *tb) +{ + int i; + int j; + + cUnk = 0; + cTod = 0; + + for (i=0; i < tb->length; i++) + { + switch (tb->status[i]) + { + case S_UNK: + cUnk++; + break; + case S_TOD: + cTod++; + break; + default: + break; + } + } + + markback(sys, tb, tb->violatedclaim); + + while (cUnk + cTod > 0) + { + while (cTod > 0) + { + for (i = 0; i < tb->length; i++) + // kies een i; laten we de eerste maar pakken + { + if (tb->status[i] == S_TOD) + break; + } + if (i == tb->length) + { + printf("Some step error.\n"); + exit(1); + } + + j = i; + while (j >= 0 && inKnowledge(tb->know[j], tb->event[i]->message)) + { + // zoek waar m in de kennis komt + j--; + } + tb->status[i] = S_OKE; + cTod--; + if (j>=0) + { + markback(sys,tb,j); + } + } + while (cTod == 0 && cUnk > 0) + { + for (i = tb->length-1; i>=0; i--) + // pak laatste i + { + if (tb->status[i] == S_UNK) + break; + } + if (i < 0) + { + printf("Some i<0 error.\n"); + exit(1); + } + + tb->status[i] = S_RED; + cUnk--; + tb->reallength--; + + j = tracebufRebuildKnow(tb); + if (j>-1) + { + tb->reallength++; + markback(sys,tb,i); + if (j < tb->length) + { + tb->link[j] = (tb->link[j] > i ? tb->link[j] : i); + } + } + } + } +} diff --git a/src/attackminimize.h b/src/attackminimize.h new file mode 100644 index 0000000..5d0e8f8 --- /dev/null +++ b/src/attackminimize.h @@ -0,0 +1 @@ +void attackMinimize(System sys, struct tracebuf *tb); diff --git a/src/cetest-cm.sh b/src/cetest-cm.sh new file mode 100755 index 0000000..6ec0a61 --- /dev/null +++ b/src/cetest-cm.sh @@ -0,0 +1,6 @@ +#!/bin/sh +# +# Test conform ce stuff, but our version +# +./scyther -r5 $* +#include +#include "tac.h" +#include "terms.h" +#include "termlists.h" +#include "runs.h" +#include "knowledge.h" +#include "symbols.h" +#include "substitutions.h" +#include "compiler.h" + +/* + Simple sys pointer as a global. Yields cleaner code although it's against programming standards. + It is declared as static to hide it from the outside world, and to indicate its status. + Other modules will just see a nicely implemented sys parameter of compile, so we can always change + it later if somebody complains. Which they won't. +*/ + +static System sys; + +/* + Forward declarations. +*/ + +void tacProcess (Tac tc); +void levelInit (void); +void levelDone (void); +Term symbolDeclare (Symbol s, int isVar); +void levelTacDeclaration (Tac tc, int isVar); +Term levelFind (Symbol s, int i); +Term symbolFind (Symbol s); +Term tacTerm (Tac tc); +Termlist tacTermlist (Tac tc); + +#define levelDeclareVar(s) levelTacDeclaration(s,1) +#define levelDeclareConst(s) levelTacDeclaration(s,0) +#define levelVar(s) symbolDeclare(s,1) +#define levelConst(s) symbolDeclare(s,0) + +/* externally used: + * TERM_Function in termlists.c for inversekeys + * TERM_Type in runs.c for type determination. + */ + +Term TERM_Agent; +Term TERM_Function; +Term TERM_Hidden; +Term TERM_Type; +Term TERM_Nonce; +Term TERM_Agent; + +Term TERM_Claim; +Term CLAIM_Secret; +Term CLAIM_Nisynch; + +/* + * Global stuff + */ + +#define MAXLEVELS 3 +static Termlist leveltl[MAXLEVELS]; +static int level; +static int maxruns; +static Protocol thisProtocol; +static Role thisRole; + +/* ------------------------------------------------------------------- */ + +/* + Compile the tac into the system +*/ + +void +compile (System mysys, Tac tc, int maxrunsset) +{ + int i; + + /* Init globals */ + maxruns = maxrunsset; + /* transfer to global static variable */ + sys = mysys; + /* init levels */ + for (i = 0; i < MAXLEVELS; i++) + leveltl[i] = NULL; + level = -1; + levelInit (); + + /* Init system constants */ +#define langhide(x,y) x = levelConst(symbolSysConst(" _" y "_ ")) +#define langtype(x,y) x->stype = termlistAdd(x->stype,y); +#define langcons(x,y,z) x = levelConst(symbolSysConst(y)); langtype(x,z) + + langhide (TERM_Type, "Type"); + langhide (TERM_Hidden, "Hidden"); + langhide (TERM_Claim, "Claim"); + + langcons (TERM_Agent, "Agent", TERM_Type); + langcons (TERM_Function, "Function", TERM_Type); + langcons (TERM_Nonce, "Nonce", TERM_Type); + + langcons (CLAIM_Secret, "Secret", TERM_Claim); + langcons (CLAIM_Nisynch, "Nisynch", TERM_Claim); + + /* process the tac */ + tacProcess (tc); + + /* cleanup */ + levelDone (); +} + +void +errorTac (int lineno) +{ + printf (" on line %i.\n", lineno); + exit (1); +} + +void +levelInit (void) +{ + level++; + if (level >= MAXLEVELS) + { + printf ("ERROR: level is increased too much\n"); + exit (1); + } + leveltl[level] = NULL; +} + +void +levelDone (void) +{ + if (level < 0) + { + printf ("ERROR: level is increased too much\n"); + exit (1); + } + leveltl[level] = NULL; + level--; +} + +Term +levelDeclare (Symbol s, int isVar, int level) +{ + Term t; + + t = levelFind (s, level); + if (t == NULL) + { + /* new! */ + if (isVar) + { + t = makeTermType (VARIABLE, s, -(level + 1)); + sys->variables = termlistAdd (sys->variables, t); + } + else + { + t = makeTermType (GLOBAL, s, -(level + 1)); + } + leveltl[level] = termlistAdd (leveltl[level], t); + + /* add to relevant list */ + switch (level) + { + case 0: + sys->locals = termlistAdd (sys->locals, t); + break; + case 1: + thisProtocol->locals = termlistAdd (thisProtocol->locals, t); + break; + case 2: + thisRole->locals = termlistAdd (thisRole->locals, t); + break; + } + } + return t; +} + +Term +symbolDeclare (Symbol s, int isVar) +{ + return levelDeclare (s, isVar, level); +} + +Term +levelFind (Symbol s, int level) +{ + Termlist tl; + + tl = leveltl[level]; + while (tl != NULL) + { + if (isTermLeaf (tl->term)) + { + if (tl->term->symb == s) + { + return tl->term; + } + } + tl = tl->next; + } + return NULL; +} + +Term +symbolFind (Symbol s) +{ + int i; + Term t; + + i = level; + while (i >= 0) + { + t = levelFind (s, i); + if (t != NULL) + return t; + i--; + } + return NULL; +} + +void +defineUsertype (Tac tcdu) +{ + Tac tc; + Term t; + Term tfind; + + tc = tcdu->tac1; + + if (tc == NULL) + { + printf ("ERROR: empty usertype declaration.\n"); + errorTac (tcdu->lineno); + } + while (tc != NULL && tc->op == TAC_STRING) + { + /* check whether this term is already declared in the same way + * (i.e. as a type) */ + + tfind = levelFind (tc->sym1, 0); + if (tfind == NULL) + { + /* this is what we expected: this type is not declared yet */ + t = levelDeclare (tc->sym1, 0, 0); + t->stype = termlistAdd (NULL, TERM_Type); + } + else + { + /* oi!, there's already one. Let's hope is is a type too. */ + if (inTermlist (tfind->stype, TERM_Type)) + { + /* phew. warn anyway */ + printf ("WARNING: double declaration of usertype "); + termPrint (tfind); + printf ("\n"); + } + else + { + /* that's not right! */ + printf ("ERROR: conflicting definitions for "); + termPrint (tfind); + printf (" in usertype definition "); + errorTac (tc->lineno); + } + } + tc = tc->next; + } +} + +void +levelTacDeclaration (Tac tc, int isVar) +{ + Tac tscan; + Termlist typetl = NULL; + Term t; + + tscan = tc->tac2; + if (!isVar && tscan->next != NULL) + { + printf ("ERROR: Multiple types not allowed for constants "); + errorTac (tscan->lineno); + } + while (tscan != NULL && tscan->op == TAC_STRING) + { + /* apparently there is type info, termlist? */ + t = levelFind (tscan->sym1, 0); + + if (t == NULL) + { + /* not declared, that is unacceptable. */ + printf ("ERROR: type "); + symbolPrint (tscan->sym1); + printf (" was not declared "); + errorTac (tscan->lineno); + } + else + { + if (!inTermlist (t->stype, TERM_Type)) + { + printf ("ERROR: non-type constant in type declaration "); + errorTac (tscan->lineno); + } + } + typetl = termlistAdd (typetl, t); + tscan = tscan->next; + } + /* parse all constants and vars */ + tscan = tc->tac1; + while (tscan != NULL) + { + t = symbolDeclare (tscan->sym1, isVar); + t->stype = typetl; + tscan = tscan->next; + } +} + +void +commEvent (int event, Tac tc) +{ + /* Add an event to the roledef, send or read */ + Term fromrole = NULL; + Term torole = NULL; + Term msg = NULL; + Term label = NULL; + Term claim = NULL; + Term claimbig = NULL; + int n = 0; + Tac trip; + + /* Construct label, if any */ + if (tc->sym1 == NULL) + { + label = NULL; + } + else + { + label = levelFind (tc->sym1, level - 1); + if (label == NULL) + { + /* effectively, labels are bound to the protocol */ + level--; + label = levelConst (tc->sym1); + level++; + } + } + trip = tc->tac2; + switch (event) + { + case READ: + case SEND: + /* now parse triplet info */ + if (trip == NULL || trip->next == NULL || trip->next->next == NULL) + { + printf ("ERROR in %i event ", event); + errorTac (tc->lineno); + } + fromrole = tacTerm (trip); + torole = tacTerm (trip->next); + msg = tacTerm (tacTuple ((trip->next->next))); + + break; + case CLAIM: + /* now parse tuple info */ + if (trip == NULL || trip->next == NULL) + { + printf ("ERROR in %i event ", event); + errorTac (tc->lineno); + } + fromrole = tacTerm (trip); + claimbig = tacTerm (tacTuple ((trip->next))); + /* check for several types */ + claim = tupleProject (claimbig, 0); + torole = claim; + /* check for obvious flaws */ + if (claim == NULL) + { + printf ("ERROR: invalid claim specification "); + errorTac (trip->next->lineno); + } + if (!inTermlist (claim->stype, TERM_Claim)) + { + printf ("ERROR: unknown claim type "); + termPrint (claim); + errorTac (trip->next->lineno); + } + /* unfold parameters to msg */ + msg = NULL; + n = tupleCount (claimbig) - 1; + if (n < 1) + { + /* no parameters */ + n = 0; + } + else + { + /* n parameters */ + msg = deVar (claimbig)->op2; + if (tupleCount (msg) != n) + { + printf + ("ERROR: something went wrong in the claim tuple parameter unfolding "); + errorTac (trip->next->lineno); + } + } + + /* handles all claim types differently */ + + if (claim == CLAIM_Secret) + { + if (n == 0) + { + printf + ("ERROR: secrecy claim requires a list of terms to be secret "); + errorTac (trip->next->lineno); + } + break; + } + if (claim == CLAIM_Nisynch) + { + if (n != 0) + { + printf ("ERROR: nisynch claim has no parameters "); + errorTac (trip->next->lineno); + } + break; + } + + /* hmm, no handler yet */ + + printf ("ERROR: No know handler for this claim type: "); + termPrint (claim); + printf (" "); + errorTac (trip->next->lineno); + break; + } + /* and make that event */ + thisRole->roledef = roledefAdd (thisRole->roledef, event, label, + fromrole, torole, msg); +} + +int +normalDeclaration (Tac tc) +{ + switch (tc->op) + { + case TAC_VAR: + levelDeclareVar (tc); + if (level < 2 && tc->tac3 == NULL) + knowledgeAddTermlist (sys->know, tacTermlist (tc->tac1)); + break; + case TAC_CONST: + levelDeclareConst (tc); + if (level < 2 && tc->tac3 == NULL) + knowledgeAddTermlist (sys->know, tacTermlist (tc->tac1)); + break; + case TAC_SECRET: + levelDeclareConst (tc); + break; + case TAC_COMPROMISED: + knowledgeAddTermlist (sys->know, tacTermlist (tc->tac1)); + break; + case TAC_INVERSEKEYS: + knowledgeAddInverse (sys->know, tacTerm (tc->tac1), tacTerm (tc->tac2)); + break; + default: + /* abort with false */ + return 0; + } + return 1; +} + +void +roleCompile (Term nameterm, Tac tc) +{ + Role r; + + /* make new (empty) current protocol with name */ + r = roleCreate (nameterm); + thisRole = r; + /* add protocol to list */ + r->next = thisProtocol->roles; + thisProtocol->roles = r; + + /* parse the content of the role */ + levelInit (); + + while (tc != NULL) + { + switch (tc->op) + { + case TAC_READ: + commEvent (READ, tc); + break; + case TAC_SEND: + commEvent (SEND, tc); + break; + case TAC_CLAIM: + commEvent (CLAIM, tc); + break; + default: + if (!normalDeclaration (tc)) + { + printf ("ERROR: illegal command %i in role ", tc->op); + termPrint (thisRole->nameterm); + printf (" "); + errorTac (tc->lineno); + } + break; + } + tc = tc->next; + } + levelDone (); +} + +void +runInstanceCreate (Tac tc) +{ + /* create an instance of an existing role + * tac1 is the dot-separated reference to the role. + * tac2 is the list of parameters to be filled in. + */ + + Protocol p; + Role r; + Symbol psym, rsym; + Termlist instParams; + + /* check whether we can still do it */ + if (sys->maxruns >= maxruns) + return; + + /* first, locate the protocol */ + psym = tc->tac1->sym1; + p = sys->protocols; + while (p != NULL && p->nameterm->symb != psym) + p = p->next; + if (p == NULL) + { + printf ("Trying to create a run of a non-declared protocol "); + symbolPrint (psym); + printf (" "); + errorTac (tc->lineno); + } + + /* locate the role */ + rsym = tc->tac1->sym2; + r = p->roles; + while (r != NULL && r->nameterm->symb != rsym) + r = r->next; + if (r == NULL) + { + printf ("Protocol "); + symbolPrint (psym); + printf (" has no role called "); + symbolPrint (rsym); + printf (" "); + errorTac (tc->lineno); + } + + /* we now know what we are instancing, equal numbers? */ + instParams = tacTermlist (tc->tac2); + if (termlistLength (instParams) != termlistLength (p->rolenames)) + { + printf + ("Run instance has different number of parameters than protocol "); + termPrint (p->nameterm); + printf (" "); + errorTac (tc->lineno); + } + + /* equal numbers, so it seems to be safe */ + roleInstance (sys, p, r, instParams); + + /* after creation analysis */ + /* AC1: untrusted agents */ + /* first: determine whether the run is untrusted, + * by checking whether one of the untrusted agents occurs + * in the run instance */ + if (untrustedAgent (sys, instParams)) + { + /* nothing yet */ + /* claims handle this themselves */ + + /* some reduction might be possible, by cutting of the last few actions + * of such an untrusted run */ + + /* but most of it might be handled dynamically */ + } + + /* AC2: originator assumption for CLP ? */ + /* TODO */ +} + +void +protocolCompile (Symbol prots, Tac tc, Tac tcroles) +{ + Protocol pr; + Term t; + + if (levelFind (prots, level) != NULL) + { + printf ("ERROR: Double declaration of protocol "); + symbolPrint (prots); + printf (" "); + errorTac (tc->lineno); + } + /* make new (empty) current protocol with name */ + pr = protocolCreate (levelConst (prots)); + thisProtocol = pr; + /* add protocol to list */ + pr->next = sys->protocols; + sys->protocols = pr; + + levelInit (); + /* add the role names */ + pr->rolenames = NULL; + while (tcroles != NULL) + { + pr->rolenames = + termlistAppend (pr->rolenames, levelConst (tcroles->sym1)); + tcroles = tcroles->next; + } + + /* parse the content of the protocol */ + while (tc != NULL) + { + switch (tc->op) + { + case TAC_UNTRUSTED: + sys->untrusted = + termlistConcat (sys->untrusted, tacTermlist (tc->tac1)); + break; + case TAC_ROLE: + t = levelFind (tc->sym1, level); + if (t != NULL) + { + roleCompile (t, tc->tac2); + } + else + { + printf ("ERROR: undeclared role "); + symbolPrint (tc->sym1); + printf (" in protocol "); + termPrint (pr->nameterm); + errorTac (tc->sym1->lineno); + } + break; + default: + if (!normalDeclaration (tc)) + { + printf ("ERROR: illegal command %i in protocol ", tc->op); + termPrint (thisProtocol->nameterm); + errorTac (tc->lineno); + } + break; + } + tc = tc->next; + } + levelDone (); +} + +void +tacProcess (Tac tc) +{ + while (tc != NULL) + { + switch (tc->op) + { + case TAC_PROTOCOL: + protocolCompile (tc->sym1, tc->tac2, tc->tac3); + break; + case TAC_UNTRUSTED: + sys->untrusted = + termlistConcat (sys->untrusted, tacTermlist (tc->tac1)); + break; + case TAC_RUN: + runInstanceCreate (tc); + break; + case TAC_USERTYPE: + defineUsertype (tc); + break; + default: + if (!normalDeclaration (tc)) + { + printf ("ERROR: illegal command %i at the global level.\n", + tc->op); + errorTac (tc->lineno); + } + break; + } + tc = tc->next; + } +} + +Term +tacTerm (Tac tc) +{ + switch (tc->op) + { + case TAC_ENCRYPT: + return makeTermEncrypt (tacTerm (tc->tac1), tacTerm (tc->tac2)); + case TAC_TUPLE: + return makeTermTuple (tacTerm (tc->tac1), tacTerm (tc->tac2)); + case TAC_STRING: + { + Term t = symbolFind (tc->sym1); + if (t == NULL) + { + printf ("Undeclared symbol "); + symbolPrint (tc->sym1); + errorTac (tc->lineno); + } + return t; + } + default: + return NULL; + } +} + +Termlist +tacTermlist (Tac tc) +{ + Termlist tl = NULL; + + while (tc != NULL) + { + tl = termlistAppend (tl, tacTerm (tc)); + tc = tc->next; + } + return tl; +} diff --git a/src/compiler.h b/src/compiler.h new file mode 100644 index 0000000..837aa93 --- /dev/null +++ b/src/compiler.h @@ -0,0 +1,6 @@ +#ifndef COMPILER +#define COMPILER + +void compile (System sys, Tac tc, int maxruns); + +#endif diff --git a/src/constraints.c b/src/constraints.c new file mode 100644 index 0000000..8066345 --- /dev/null +++ b/src/constraints.c @@ -0,0 +1,336 @@ +#include +#include "memory.h" +#include "constraints.h" +#include "debug.h" +#include "runs.h" + + + +/* constraints currently are shallow copies */ + +Constraint +makeConstraint (Term term, Knowledge know) +{ + /* maybe knowDup can just be a link, but then it needs to be moved from destroy as well */ + Constraint co = memAlloc (sizeof (struct constraint)); + co->term = term; + //co->know = knowledgeDuplicate(know); + co->know = know; + return co; +} + + +Constraint +constraintDuplicate (Constraint co) +{ + return makeConstraint (co->term, co->know); +} + + +void +constraintDestroy (Constraint cons) +{ + //knowledgeDelete(cons->know); + if (cons != NULL) + memFree (cons, sizeof (struct constraint)); +} + +/* constraints are typically added at the end, to maintain the order in which they were added */ + +Constraintlist +constraintlistAdd (Constraintlist cl, Constraint co) +{ + Constraintlist clnew = memAlloc (sizeof (struct constraintlist)); + + clnew->constraint = co; + clnew->next = NULL; + if (cl == NULL) + { + clnew->prev = NULL; + return clnew; + } + else + { + Constraintlist scan; + + scan = cl; + while (scan->next != NULL) + scan = scan->next; + scan->next = clnew; + clnew->prev = scan; + return cl; + } +} + +Constraintlist +constraintlistConcat (Constraintlist cl1, Constraintlist cl2) +{ + Constraintlist scan; + + if (cl1 == NULL) + return cl2; + scan = cl1; + while (scan->next != NULL) + scan = scan->next; + scan->next = cl2; + return cl1; +} + +Constraintlist +constraintlistRewind (Constraintlist cl) +{ + if (cl == NULL) + return NULL; + while (cl->prev != NULL) + cl = cl->prev; + return cl; +} + + +Constraintlist +constraintlistInsert (Constraintlist cl, Term term, Knowledge know) +{ + Constraintlist clnew = memAlloc (sizeof (struct constraintlist)); + + clnew->constraint = makeConstraint (term, know); + if (cl != NULL) + { + if (cl->next != NULL) + { + clnew->next = cl->next; + cl->next->prev = cl; + } + else + { + clnew->next = NULL; + } + clnew->prev = cl; + cl->next = clnew; + return constraintlistRewind (cl); + } + else + { + clnew->next = NULL; + clnew->prev = NULL; + return clnew; + } +} + +/* unlink a single constraint */ + +Constraintlist +constraintlistUnlink (Constraintlist cl) +{ + Constraintlist clnext, clprev; + + if (cl == NULL) + return NULL; + clprev = cl->prev; + clnext = cl->next; + + if (clnext != NULL) + { + clnext->prev = clprev; + cl->next = NULL; + } + if (clprev != NULL) + { + clprev->next = clnext; + cl->prev = NULL; + return constraintlistRewind (clprev); + } + else + { + return clnext; + } +} + + +/* remove a single constraint */ + +Constraintlist +constraintlistRemove (Constraintlist cl) +{ + Constraintlist clnew; + + clnew = constraintlistUnlink (cl); + memFree (cl, sizeof (struct constraintlist)); + return clnew; +} + +/* remove all constraints from this point onwards */ + +void +constraintlistDelete (Constraintlist cl) +{ + Constraintlist cldel; + + /* no empty cl */ + if (cl == NULL) + return; + + /* cut off previous */ + if (cl->prev != NULL) + { + /* TODO maybe this should cause a warning? */ + printf ("WARNING: clDelete with non-empty prev\n"); + cl->prev->next = NULL; + } + while (cl != NULL) + { + cldel = cl; + cl = cl->next; + memFree (cldel, sizeof (struct constraintlist)); + } + return; +} + +void +constraintlistDestroy (Constraintlist cl) +{ + Constraintlist cldel; + + /* no empty cl */ + if (cl == NULL) + return; + + /* cut off previous */ + if (cl->prev != NULL) + { + /* TODO maybe this should cause a warning? */ + printf ("WARNING: clDestroy with non-empty prev\n"); + cl->prev = NULL; + } + while (cl != NULL) + { + cldel = cl; + cl = cl->next; + constraintDestroy (cldel->constraint); + memFree (cldel, sizeof (struct constraintlist)); + } +} + + +Constraintlist +constraintlistDuplicate (Constraintlist oldcl) +{ + Constraintlist newcl = NULL; + + while (oldcl != NULL) + { + newcl = + constraintlistAdd (newcl, constraintDuplicate (oldcl->constraint)); + oldcl = oldcl->next; + } + return newcl; +} + +Constraintlist +constraintlistShallow (Constraintlist oldcl) +{ + Constraintlist newcl = NULL; + + while (oldcl != NULL) + { + newcl = constraintlistAdd (newcl, oldcl->constraint); + oldcl = oldcl->next; + } + return newcl; +} + +/* ---------------------------------------------------------- + + Print stuff + +---------------------------------------------------------- */ + +void +constraintPrint (Constraint co) +{ + indent (); + printf ("Constraint "); + if (co == NULL) + { + printf ("[empty]\n"); + return; + } + termPrint (co->term); + printf (" :\n"); + knowledgePrint (co->know); +} + +void +constraintlistPrint (Constraintlist cl) +{ + if (cl == NULL) + { + indent (); + printf ("[empty constraintlist]\n"); + return; + } + while (cl != NULL) + { + constraintPrint (cl->constraint); + cl = cl->next; + } +} + + +/* ---------------------------------------------------------- + + Now some real logic for the constraints + +---------------------------------------------------------- */ + +/* eliminate all standalone variables */ + +void +msElim (Constraint co) +{ + Termlist tl; + + /* simple variables can only exist in basic */ + if (co->know == NULL) + { +#ifdef DEBUG + debug (5, "Exiting because co->know is empty."); +#endif + } + else + { + tl = co->know->basic; + while (tl != NULL) + { + if (isTermVariable (tl->term)) + { + tl = termlistDelTerm (tl); + co->know->basic = tl; + } + else + tl = tl->next; + } + } +} + + +/* find the first constraint such that m is not a variable */ +/* also, apply standalone elimination to it */ + +Constraintlist +firstNonVariable (Constraintlist cl) +{ + while (cl != NULL && isTermVariable (cl->constraint->term)) + { + cl = cl->next; + } + if (cl != NULL) + { + msElim (cl->constraint); + cl->constraint->term = deVar (cl->constraint->term); + return cl; + } + else + { + return NULL; + } +} diff --git a/src/constraints.h b/src/constraints.h new file mode 100644 index 0000000..ff5f541 --- /dev/null +++ b/src/constraints.h @@ -0,0 +1,42 @@ +#ifndef CONSTRAINTS +#define CONSTRAINTS +#include "terms.h" +#include "knowledge.h" + +struct constraint +{ + Term term; + Knowledge know; +}; + +typedef struct constraint *Constraint; + +struct constraintlist +{ + Constraint constraint; + struct constraintlist *next; + struct constraintlist *prev; +}; + +typedef struct constraintlist *Constraintlist; + +Constraint makeConstraint (Term term, Knowledge know); +Constraint constraintDuplicate (Constraint co); +void constraintDestroy (Constraint cons); +Constraintlist constraintlistAdd (Constraintlist cl, Constraint co); +Constraintlist constraintlistConcat (Constraintlist cl1, Constraintlist cl2); +Constraintlist constraintlistRewind (Constraintlist cl); +Constraintlist constraintlistInsert (Constraintlist cl, Term term, + Knowledge know); +Constraintlist constraintlistUnlink (Constraintlist cl); +Constraintlist constraintlistRemove (Constraintlist cl); +void constraintlistDestroy (Constraintlist cl); +void constraintlistDelete (Constraintlist cl); +Constraintlist constraintlistShallow (Constraintlist oldcl); +Constraintlist constraintlistDuplicate (Constraintlist oldcl); +void constraintPrint (Constraint co); +void constraintlistPrint (Constraintlist cl); + +Constraintlist firstNonVariable (Constraintlist cl); + +#endif diff --git a/src/debug.c b/src/debug.c new file mode 100644 index 0000000..401a946 --- /dev/null +++ b/src/debug.c @@ -0,0 +1,30 @@ +#include +#include +#include "debug.h" +#include "runs.h" + +static int debuglevel; + +void +debugSet (int level) +{ + debuglevel = level; +} + +int +debugCond (int level) +{ + return (level <= debuglevel); +} + +void +debug (int level, char *string) +{ +#ifdef DEBUG + if (debugCond (level)) + { + indent (); + printf ("DEBUG [%i]: %s\n", level, string); + } +#endif +} diff --git a/src/debug.h b/src/debug.h new file mode 100644 index 0000000..2e3253d --- /dev/null +++ b/src/debug.h @@ -0,0 +1,10 @@ +#ifndef DEBUG_H +#define DEBUG_H + +void debugSet (int level); +int debugCond (int level); +void debug (int level, char *string); + +#define DEBUGL(a) debugCond(a) + +#endif diff --git a/src/debuglevels.txt b/src/debuglevels.txt new file mode 100644 index 0000000..873b4e4 --- /dev/null +++ b/src/debuglevels.txt @@ -0,0 +1,7 @@ +Conventions for debug levels; ifdef DEBUG + +-D1 meta show compiled code, meta dynamic results +-D2 claims show results of claims +-D3 events show executed events +-D4 internalsHigh interesting internals (traces that were cut off) +-D5 internalsLow usually uninteresting stuff diff --git a/src/design.txt b/src/design.txt new file mode 100644 index 0000000..97c40bb --- /dev/null +++ b/src/design.txt @@ -0,0 +1,47 @@ +Design Issues for the Model Checker +----------------------------------- + +- For secrecy, we can trigger all sends at once. For synchronisation, + this is not the case. + +- Modules have to be split up sensibly. + + - Although 'knowledge' and 'term matching' seem to different items, + their intertwined workings suggest that they should be implemented + in parallel. + + - State generation (as in creating instances) might already allow for + a lot of static analysis. + +- We should make a list of required operations. Ingmar's work can serve + as a starting point. + +- For now, there will be no parser, and test cases will be input by + hand. + +- Synchronisation is more difficult to test, so we focus on secrecy + first. I've got some good ideas on Synchronisation testing though + (with narrowing sets of possible partners). Synchronisation is very + hard to prune, I presume. I have to prove that though ;) + +Sketch for secrecy: + +SimulateStep(F,M,constraints): + if Empty(F): + return + else: + for (s in PossibleSends): + add s.message to M + if (secrecy violated): + halt + remove s from F + ReadSets = supersetTransitions(F) + for (ReadSet in ReadSets): + for (s in ReadSet): + // dit is vaag + if NonMatch goto next ReadSet + constraint = F,M,match() + SimulateStep(F \ s,M,constraints) + + + diff --git a/src/knowledge.c b/src/knowledge.c new file mode 100644 index 0000000..1816106 --- /dev/null +++ b/src/knowledge.c @@ -0,0 +1,474 @@ +#include +#include +#include "termlists.h" +#include "knowledge.h" +#include "memory.h" +#include "runs.h" +#include "debug.h" + +/* + * Knowledge stuff + * + * Note that a really weird thing is going on involving unpropagated substitutions. + * Idea: + * + * 1. Substitute terms by filling in ->subst. + * Now, either: + * 2a. Undo this by knowledgeUndo. + * 2b. Propagate it, modifying the knowledge beyond repair by knowledgeSubstDo. Now inKnowledge works again. + * 2c. inKnowledge/knowledgeSet if something is in the knowledge: this does not consider the substitutions!, and + * they now have some overhead. + */ + +void +knowledgeInit (void) +{ + return; +} + +void +knowledgeDone (void) +{ +} + +Knowledge +makeKnowledge () +{ + return (Knowledge) memAlloc (sizeof (struct knowledge)); +} + +Knowledge +emptyKnowledge () +{ + Knowledge know; + + know = makeKnowledge (); + know->basic = NULL; + know->encrypt = NULL; + know->inverses = NULL; + know->vars = NULL; + return know; +} + +Knowledge +knowledgeDuplicate (Knowledge know) +{ + Knowledge newknow; + + if (know == NULL) + { + printf ("Warning! Trying to copy empty knowledge!\n"); + return NULL; + } + newknow = makeKnowledge (); + newknow->basic = termlistShallow (know->basic); + newknow->encrypt = termlistShallow (know->encrypt); + newknow->vars = termlistShallow (know->vars); + newknow->inverses = know->inverses; + return newknow; +} + +void +knowledgeDelete (Knowledge know) +{ + if (know != NULL) + { + termlistDelete (know->basic); + termlistDelete (know->encrypt); + termlistDelete (know->vars); + memFree (know, sizeof (struct knowledge)); + } +} + +void +knowledgeDestroy (Knowledge know) +{ + if (know != NULL) + { + termlistDestroy (know->basic); + termlistDestroy (know->encrypt); + termlistDestroy (know->vars); + // termlistDestroy(know->inverses); + memFree (know, sizeof (struct knowledge)); + } +} + +/* + * knowledgeAddTerm + * + * returns a boolean: + * true iff the term was actually new, and added. + */ + +int +knowledgeAddTerm (Knowledge know, Term term) +{ + if (know == NULL) + { + printf + ("Warning: trying to add term to uninitialised (NULL) Know pointer.\n"); + return 1; + } + if (term == NULL) + return 0; + + term = deVar (term); + + /* test whether we knew it before */ + if (inKnowledge (know, term)) + return 0; + + if (isTermTuple (term)) + { + knowledgeAddTerm (know, term->op1); + knowledgeAddTerm (know, term->op2); + } + + /* adding variables? */ + know->vars = termlistAddVariables (know->vars, term); + + knowledgeSimplify (know, term); + if (isTermLeaf (term)) + { + know->basic = termlistAdd (know->basic, term); + } + if (term->type == ENCRYPT) + { + Term invkey = inverseKey (know->inverses, term->key); + if (inKnowledge (know, invkey)) + { + /* we can decrypt it */ + knowledgeAddTerm (know, term->op); + if (!inKnowledge (know, term->key)) + { + /* we know the op now, but not the key, so add it anyway */ + know->encrypt = termlistAdd (know->encrypt, term); + } + } + else + { + /* we cannot decrypt it, and from the initial test we know we could not construct it */ + know->encrypt = termlistAdd (know->encrypt, term); + } + termDelete (invkey); + } + return 1; +} + + +/* + Note: the input is a key k, i.e. it can decrypt + anything that was encrypted with k^{-1}. +*/ + +void +knowledgeSimplify (Knowledge know, Term key) +{ + Termlist tldecrypts = NULL; + Termlist scan = know->encrypt; + Term invkey = inverseKey (know->inverses, key); + + while (scan != NULL) + { + if (isTermEqual ((scan->term)->key, invkey)) + { + tldecrypts = termlistAdd (tldecrypts, (scan->term)->op); + know->encrypt = termlistDelTerm (scan); + scan = know->encrypt; + } + else + scan = scan->next; + } + termDelete (invkey); + knowledgeAddTermlist (know, tldecrypts); + termlistDelete (tldecrypts); +} + +/* + * Add a whole termlist. + * + * Returns true iff there was at least one new item. + */ + +int +knowledgeAddTermlist (Knowledge know, Termlist tl) +{ + int flag = 0; + + while (tl != NULL) + { + flag = knowledgeAddTerm (know, tl->term) || flag; + tl = tl->next; + } + return flag; +} + +/* + + add an inverse pair to the knowledge + + */ + +void +knowledgeAddInverse (Knowledge know, Term t1, Term t2) +{ + know->inverses = termlistAdd (know->inverses, t1); + know->inverses = termlistAdd (know->inverses, t2); + return; +} + +/* + same, but for list. List pointer is simply copied, so don't delete it later! +*/ + +void +knowledgeSetInverses (Knowledge know, Termlist tl) +{ + know->inverses = tl; +} + +/* + +inKnowledge + +Is a term a part of the knowledge? + +*/ + +int +inKnowledge (const Knowledge know, Term term) +{ + /* if there is no term, then it's okay 'fur sure' */ + if (term == NULL) + return 1; + /* if there is a term, but no knowledge, we're in trouble */ + if (know == NULL) + return 0; + + mindwipe (know, inKnowledge (know, term)); + + term = deVar (term); + if (isTermLeaf (term)) + { + return inTermlist (know->basic, term); + } + if (term->type == ENCRYPT) + { + return inTermlist (know->encrypt, term) || + (inKnowledge (know, term->key) && inKnowledge (know, term->op)); + } + if (term->type == TUPLE) + { + return (inTermlist (know->encrypt, term) || + (inKnowledge (know, term->op1) && + inKnowledge (know, term->op2))); + } + return 0; /* unrecognized term type, weird */ +} + +int +isKnowledgeEqual (Knowledge know1, Knowledge know2) +{ + if (know1 == NULL || know2 == NULL) + { + if (know1 == NULL && know2 == NULL) + return 1; + else + return 0; + } + if (!isTermlistEqual (know1->encrypt, know2->encrypt)) + return 0; + return isTermlistEqual (know1->basic, know2->basic); +} + + +void +knowledgePrint (Knowledge know) +{ + indent (); + if (know == NULL) + { + printf ("Empty.\n"); + return; + } + printf (" [Basic]: "); + termlistPrint (know->basic); + printf ("\n"); + indent (); + printf (" [Encrp]: "); + termlistPrint (know->encrypt); + printf ("\n"); + indent (); + printf (" [Vars]: "); + termlistPrint (know->vars); + printf ("\n"); +} + +/* + print inverses + */ + +void +knowledgeInversesPrint (Knowledge know) +{ + Termlist tl; + int after = 0; + + if (know == NULL) + { + printf ("Empty knowledge."); + return; + } + + tl = knowledgeGetInverses (know); + if (tl == NULL) + { + printf ("None."); + } + else + { + while (tl != NULL && tl->next != NULL) + { + if (after) + { + printf (","); + } + printf ("("); + termPrint (tl->term); + printf (","); + termPrint (tl->next->term); + printf (")"); + after = 1; + tl = tl->next->next; + } + } +} + +/* + give the set of representatives for the knowledge. + Note: this is a shallow copy, and needs to be termlistDelete'd. + */ + +Termlist +knowledgeSet (Knowledge know) +{ + Termlist tl1, tl2; + + tl1 = termlistShallow (know->basic); + tl2 = termlistShallow (know->encrypt); + return termlistConcat (tl1, tl2); +} + +/* + get the inverses pointer of the knowledge. + Essentially the inverse function of knowledgeSetInverses +*/ + +Termlist +knowledgeGetInverses (Knowledge know) +{ + if (know == NULL) + return NULL; + else + return know->inverses; +} + +/* + * check whether any substitutions where made at all. + */ + +int +knowledgeSubstNeeded (const Knowledge know) +{ + Termlist tl; + + if (know == NULL) + return 0; + tl = know->vars; + while (tl != NULL) + { + if (tl->term->subst != NULL) + return 1; + tl = tl->next; + } + return 0; +} + +/* + * knowledgeReconstruction + * + * This is useful after e.g. substitutions. + * Just rebuilds the knowledge in a new (shallow) copy. + */ + +Knowledge +knowledgeReconstruction (const Knowledge know) +{ + Knowledge newknow = emptyKnowledge (); + + newknow->inverses = know->inverses; + knowledgeAddTermlist (newknow, know->basic); + knowledgeAddTermlist (newknow, know->encrypt); + return newknow; +} + +/* + * propagate any substitutions just made. + * + * This usually involves reconstruction of the complete knowledge, which is + * 'cheaper' than a thorough analysis, so we always make a copy. + */ + +Knowledge +knowledgeSubstDo (const Knowledge know) +{ + /* otherwise a copy (for deletion) is returned. */ + return knowledgeReconstruction (know); +} + +/* + * Undo the substitutions just made. Note that this does not work anymore after knowledgeSubstDo! + */ + +void +knowledgeSubstUndo (const Knowledge know) +{ + Termlist tl; + + tl = know->vars; + while (tl != NULL) + { + tl->term->subst = NULL; + tl = tl->next; + } +} + +/* + * knowledgeNew(old,new) + * + * yield a termlist (or NULL) that represents the reduced items that are + * in the new set, but not in the old one. + */ + +Termlist +knowledgeNew (const Knowledge oldk, const Knowledge newk) +{ + Termlist newtl; + + newtl = NULL; + + void addNewStuff (Termlist tl) + { + while (tl != NULL) + { + if (!inKnowledge (oldk, tl->term)) + { + newtl = termlistAdd (newtl, tl->term); + } + tl = tl->next; + } + } + addNewStuff (newk->basic); + addNewStuff (newk->encrypt); + return newtl; +} diff --git a/src/knowledge.h b/src/knowledge.h new file mode 100644 index 0000000..3005da2 --- /dev/null +++ b/src/knowledge.h @@ -0,0 +1,60 @@ +#ifndef KNOWLEDGE +#define KNOWLEDGE + +#include "terms.h" +#include "termlists.h" + +struct knowledge +{ + Termlist basic; + Termlist encrypt; + Termlist inverses; + union + { + Termlist vars; // special: denotes unsubstituted variables + struct knowledge *next; // use for alternative memory management. + }; +}; + +typedef struct knowledge *Knowledge; + +void knowledgeInit (void); +void knowledgeDone (void); +Knowledge makeKnowledge (); +Knowledge emptyKnowledge (); +Knowledge knowledgeDuplicate (Knowledge know); +void knowledgeDelete (Knowledge know); +void knowledgeDestroy (Knowledge know); +int knowledgeAddTerm (Knowledge know, Term term); +int knowledgeAddTermlist (Knowledge know, Termlist tl); +void knowledgeAddInverse (Knowledge know, Term t1, Term t2); +void knowledgeSetInverses (Knowledge know, Termlist tl); +void knowledgeSimplify (Knowledge know, Term decryptkey); +int inKnowledge (const Knowledge know, Term term); +void knowledgePrint (Knowledge know); +void knowledgeInversesPrint (Knowledge know); +int isKnowledgeEqual (Knowledge know1, Knowledge know2); +Termlist knowledgeSet (Knowledge know); +Termlist knowledgeGetInverses (Knowledge know); +int knowledgeSubstNeeded (const Knowledge know); +Knowledge knowledgeSubstDo (const Knowledge know); +void knowledgeSubstUndo (const Knowledge know); +Termlist knowledgeNew (const Knowledge oldk, const Knowledge newk); + +#define mindwipe(k,recurse) \ + if (k != NULL && k->vars != NULL) { \ + Termlist tl = k->vars; \ + while (tl != NULL) { \ + if (tl->term->subst != NULL) { \ + Term oldsubst = tl->term->subst; \ + tl->term->subst = NULL; \ + int flag = recurse; \ + tl->term->subst = oldsubst; \ + return flag; \ + } \ + tl = tl->next; \ + } \ + } \ + + +#endif diff --git a/src/language.txt b/src/language.txt new file mode 100644 index 0000000..a3e84ba --- /dev/null +++ b/src/language.txt @@ -0,0 +1,46 @@ +Language +-------- + +language := ()* +def := ( | | | ) + +//directive := +//dir_require := require ; + +protocol := protocol ( ) { * } optsc +protocolname := + +intruderknow := public ; +roledef := role { } optsc +rolename := +actions := (;)+ +action := ( | | ) +decl := ( | )+ +const := const [ : ]; +var := var [ : ]; + +read := read [_