Пример #1
0
//! Determine whether to show an event
int
isEventInteresting (const System sys, const Roledef rd)
{
  if (switches.human)
    {
      if (rd->type != CLAIM)
	{
	  return 1;
	}
      else
	{
	  // A claim
	  if (only_claim_label == NULL)
	    {
	      return 1;
	    }
	  else
	    {
	      if (isTermEqual (only_claim_label, rd->label))
		{
		  return 1;
		}
	    }
	}
      return 0;
    }
  else
    {
      return 1;
    }
}
Пример #2
0
/**
 *@return Adds f(x)=y to an existing function f. If f is NULL, a function is created. If x is already in the domain, the value is replaced.
 */
Termmap
termmapSet (const Termmap f, const Term x, const int y)
{
  Termmap fscan;

  //! Determine whether term already occurs
  fscan = f;
  while (fscan != NULL)
    {
      if (isTermEqual (x, fscan->term))
	{
	  //! Is the result correct already?
	  if (fscan->result != y)
	    fscan->result = y;
	  return f;
	}
      fscan = fscan->next;
    }
  //! Not occurred yet, make new node
  fscan = makeTermmap ();
  fscan->term = x;
  fscan->result = y;
  fscan->next = f;
  return fscan;
}
Пример #3
0
/**
 * Currently, inequality constraints are encoded using "NotEqual" claims.
 *
 * Here we check that their arguments have not become equal. If they are not
 * equal, there always exists a solution in which the values are different. The
 * solution generated by the algorithm that grounds the trace (for
 * visualisation) yields a compatible solution.
 *
 * Return true if okay - constraints can be met
 * Return false if not okay - at least one constraint violated
 *
 * Note that this function performs its own proof output if needed.
 * This allows it to pinpoint the exact constraint that is violated.
 *
 * Speed: this is certainly not the most efficient way to solve this. We are
 * looping over all regular events, even if there are not negative constraints
 * at all. Instead, we could simply collect a list of all negative constraints,
 * which would speed up iterating over it.
 */
int
inequalityConstraints (const System sys)
{
  int run;

  for (run = 0; run < sys->maxruns; run++)
    {
      if (sys->runs[run].protocol != INTRUDER)
	{
	  int e;
	  Roledef rd;

	  rd = sys->runs[run].start;
	  for (e = 0; e < sys->runs[run].step; e++)
	    {
	      if (rd->type == CLAIM)
		{
		  // It's a claim
		  if (isTermEqual (rd->claiminfo->type, CLAIM_Notequal))
		    {
		      // TODO ASSERT: Message should be a pair for NotEqual claims
		      if (isTermEqual
			  (TermOp1 (rd->message), TermOp2 (rd->message)))
			{
			  // Inequality violated, no solution exists that makes them inequal anymore.
			  if (switches.output == PROOF)
			    {
			      indentPrint ();
			      eprintf
				("Pruned because the pattern violates an inequality constraint based on the term ");
			      termPrint (TermOp1 (rd->message));
			      eprintf (".\n");
			    }

			  return false;
			}
		    }
		}
	      rd = rd->next;
	    }
	}
    }
  return true;
}
Пример #4
0
/**
 * Return true if should be pruned
 *
 * The termlist 'agentrole' contains subsequences of length 2, the first of which is a agent name, and the second is the role it is performing.
 * The idea is that once the agent name is assigned, it should not occur again for a different role.
 * E.g. [ alice, initiator, bob, responder, charlie, ... ]
 */
int
multipleRolePrune (const System sys)
{
  Termlist agentrole;
  int run;

  agentrole = NULL;
  for (run = 0; run < sys->maxruns; run++)
    {
      Protocol p;

      p = sys->runs[run].protocol;
      if ((p != INTRUDER) && (!isHelperProtocol (p)))
	{
	  Term rolename;
	  Term agent;
	  Termlist tl;

	  rolename = sys->runs[run].role->nameterm;
	  agent = agentOfRun (sys, run);

	  // Does this agent already occur yet in the list?
	  for (tl = agentrole; tl != NULL; tl = (tl->next)->next)
	    {
	      if (isTermEqual (agent, tl->term))
		{
		  if (!isTermEqual (rolename, (tl->next)->term))
		    {
		      // Same agent, but different role! This is not allowed.
		      termlistDelete (agentrole);	// cleanup
		      return true;
		    }
		}
	    }
	  // Does not occur yet, so add
	  // Note we add the elements in front, so we need to reverse the order
	  agentrole = termlistPrepend (agentrole, rolename);
	  agentrole = termlistPrepend (agentrole, agent);
	}
    }
  termlistDelete (agentrole);
  return false;
}
Пример #5
0
/**
 *@return Yields f(x), or -1 when it is not present.
 */
int
termmapGet (Termmap f, const Term x)
{
  while (f != NULL)
    {
      if (isTermEqual (x, f->term))
	return f->result;
      f = f->next;
    }
  return -1;
}
Пример #6
0
//! Given a list of label infos, yield the correct one or NULL
Labelinfo
label_find (List labellist, const Term label)
{
  Labelinfo linfo;

  int label_find_scan (void *data)
  {
    Labelinfo linfo_scan;

    linfo_scan = (Labelinfo) data;
    if (isTermEqual (label, linfo_scan->label))
      {
	linfo = linfo_scan;
	return 0;
      }
    else
      {
	return 1;
      }
  }
Пример #7
0
/**
 * Try to determine the most general unifier of two terms.
 * Resulting termlist must be termlistDelete'd.
 *
 *@return Returns a list of variables, that were previously open, but are now closed
 * in such a way that the two terms unify. Returns \ref MGUFAIL if it is impossible.
 * The termlist should be deleted.
 *
 * @TODO this code should be removed, as it duplicates 'unify' code, and is
 * ill-suited for adaption later on with multiple unifiers.
 */
Termlist
termMguTerm (Term t1, Term t2)
{
  /* added for speed */
  t1 = deVar (t1);
  t2 = deVar (t2);
  if (t1 == t2)
    return NULL;

  if (!(hasTermVariable (t1) || hasTermVariable (t2)))
    {
      if (isTermEqual (t1, t2))
	{
	  return NULL;
	}
      else
	{
	  return MGUFAIL;
	}
    }

  /*
   * Distinguish a special case where both are unbound variables that will be
   * connected, and I want to give one priority over the other for readability.
   *
   * Because t1 and t2 have been deVar'd means that if they are variables, they
   * are also unbound.
   */

  if (realTermVariable (t1) && realTermVariable (t2) && goodsubst (t1, t2))
    {
      /* Both are unbound variables. Decide.
       *
       * The plan: t1->subst will point to t2. But maybe we prefer the other
       * way around?
       */
      if (preferSubstitutionOrder (t2, t1))
	{
	  Term t3;

	  // Swappy.
	  t3 = t1;
	  t1 = t2;
	  t2 = t3;
	}
      t1->subst = t2;
#ifdef DEBUG
      showSubst (t1);
#endif
      return termlistAdd (NULL, t1);
    }

  /* symmetrical tests for single variable.
   */

  if (realTermVariable (t2))
    {
      if (termSubTerm (t1, t2) || !goodsubst (t2, t1))
	return MGUFAIL;
      else
	{
	  t2->subst = t1;
#ifdef DEBUG
	  showSubst (t2);
#endif
	  return termlistAdd (NULL, t2);
	}
    }
  if (realTermVariable (t1))
    {
      if (termSubTerm (t2, t1) || !goodsubst (t1, t2))
	return MGUFAIL;
      else
	{
	  t1->subst = t2;
#ifdef DEBUG
	  showSubst (t1);
#endif
	  return termlistAdd (NULL, t1);
	}
    }

  /* left & right are compounds with variables */
  if (t1->type != t2->type)
    return MGUFAIL;

  /* identical compounds */
  /* encryption first */
  if (realTermEncrypt (t1))
    {
      Termlist tl1, tl2;

      tl1 = termMguTerm (TermKey (t1), TermKey (t2));
      if (tl1 == MGUFAIL)
	{
	  return MGUFAIL;
	}
      else
	{
	  tl2 = termMguTerm (TermOp (t1), TermOp (t2));
	  if (tl2 == MGUFAIL)
	    {
	      termlistSubstReset (tl1);
	      termlistDelete (tl1);
	      return MGUFAIL;
	    }
	  else
	    {
	      return termlistConcat (tl1, tl2);
	    }
	}
    }

  /* tupling second
     non-associative version ! TODO other version */
  if (isTermTuple (t1))
    {
      Termlist tl1, tl2;

      tl1 = termMguTerm (TermOp1 (t1), TermOp1 (t2));
      if (tl1 == MGUFAIL)
	{
	  return MGUFAIL;
	}
      else
	{
	  tl2 = termMguTerm (TermOp2 (t1), TermOp2 (t2));
	  if (tl2 == MGUFAIL)
	    {
	      termlistSubstReset (tl1);
	      termlistDelete (tl1);
	      return MGUFAIL;
	    }
	  else
	    {
	      return termlistConcat (tl1, tl2);
	    }
	}
    }
  return MGUFAIL;
}
Пример #8
0
/**
 * Try to determine the most general unifier of two terms, if so calls function.
 *
 * int callback(Termlist, *state)
 *
 * The callback receives a list of variables, that were previously open, but are now closed
 * in such a way that the two terms unify. 
 *
 * The callback must return true for the iteration to proceed: if it returns false, a single call would abort the scan.
 * The return value shows this: it is false if the scan was aborted, and true if not.
 */
int
unify (Term t1, Term t2, Termlist tl, int (*callback) (), void *state)
{
  /* added for speed */
  t1 = deVar (t1);
  t2 = deVar (t2);
  if (t1 == t2)
    {
      return callback (tl, state);
    }

  if (!(hasTermVariable (t1) || hasTermVariable (t2)))
    {
      // None has a variable
      if (isTermEqual (t1, t2))
	{
	  // Equal!
	  return callback (tl, state);
	}
      else
	{
	  // Can never be fixed, no variables
	  return true;
	}
    }

  /*
   * Distinguish a special case where both are unbound variables that will be
   * connected, and I want to give one priority over the other for readability.
   *
   * Because t1 and t2 have been deVar'd means that if they are variables, they
   * are also unbound.
   */

  if (realTermVariable (t1) && realTermVariable (t2) && goodsubst (t1, t2))
    {
      /* Both are unbound variables. Decide.
       *
       * The plan: t1->subst will point to t2. But maybe we prefer the other
       * way around?
       */
      if (preferSubstitutionOrder (t2, t1))
	{
	  Term t3;

	  // Swappy.
	  t3 = t1;
	  t1 = t2;
	  t2 = t3;
	}
      return callsubst (callback, state, tl, t1, t2);
    }

  /* symmetrical tests for single variable.
   */

  if (realTermVariable (t2))
    {
      if (termSubTerm (t1, t2) || !goodsubst (t2, t1))
	return true;
      else
	{
	  return callsubst (callback, state, tl, t2, t1);
	}
    }
  if (realTermVariable (t1))
    {
      if (termSubTerm (t2, t1) || !goodsubst (t1, t2))
	return true;
      else
	{
	  return callsubst (callback, state, tl, t1, t2);
	}
    }

  /* left & right are compounds with variables */
  if (t1->type != t2->type)
    return true;

  /* identical compound types */

  /* encryption first */
  if (realTermEncrypt (t1))
    {
      struct state_mgu_tmp tmpstate;

      tmpstate.oldstate = state;
      tmpstate.oldcallback = callback;
      tmpstate.unifyt1 = TermOp (t1);
      tmpstate.unifyt2 = TermOp (t2);

      return unify (TermKey (t1), TermKey (t2), tl, unify_callback_wrapper,
		    &tmpstate);
    }

  /* tupling second
     non-associative version ! TODO other version */
  if (isTermTuple (t1))
    {
      struct state_mgu_tmp tmpstate;

      tmpstate.oldstate = state;
      tmpstate.oldcallback = callback;
      tmpstate.unifyt1 = TermOp2 (t1);
      tmpstate.unifyt2 = TermOp2 (t2);

      return unify (TermOp1 (t1), TermOp1 (t2), tl, unify_callback_wrapper,
		    &tmpstate);
    }

  return true;
}
Пример #9
0
/**
 * When something is pruned because of this function, the state space is still
 * considered to be complete.
 *
 *@returns true iff this state is invalid because of a theorem
 */
int
prune_theorems (const System sys)
{
  List bl;
  int run;

  // Check all types of the local agents according to the matching type
  if (!checkAllSubstitutions (sys))
    {
      if (switches.output == PROOF)
	{
	  indentPrint ();
	  eprintf
	    ("Pruned because some local variable was incorrectly substituted.\n");
	}
      return true;
    }

  // Prune if agents are disallowed from performing multiple roles
  if (switches.oneRolePerAgent != 0)
    {
      if (multipleRolePrune (sys))
	{
	  if (switches.output == PROOF)
	    {
	      indentPrint ();
	      eprintf
		("Pruned because an agent may not perform multiple roles.\n");
	    }
	  return true;
	}
    }

  // Prune if any initiator run talks to itself
  /**
   * This effectively disallows Alice from talking to Alice, for all
   * initiators. We still allow it for responder runs, because we assume the
   * responder is not checking this.
   */
  if (switches.initUnique)
    {
      if (selfInitiators (sys) > 0)
	{
	  // XXX TODO
	  // Still need to fix proof output for this
	  //
	  // Pruning because some agents are equal for this role.
	  return true;
	}
    }

  if (switches.respUnique)
    {
      if (selfResponders (sys) > 0)
	{
	  // XXX TODO
	  // Still need to fix proof output for this
	  //
	  // Pruning because some agents are equal for this role.
	  return true;
	}
    }

  if (switches.roleUnique)
    {
      if (!agentsUniqueRoles (sys))
	{
	  if (switches.output == PROOF)
	    {
	      indentPrint ();
	      eprintf
		("Pruned because agents are not performing unique roles.\n");
	    }
	  return true;
	}
    }

/*
The semantics imply that create event chose agent names, i.e., the range of rho is a subset of Agent.

For chosen name attacks we may want to loosen that. However, this requires inserting receive events for the non-actor role variables of responders, and we don't have that yet,
so technically this is a bug. Don't use.
*/
  if (switches.chosenName)
    {
      // Check if all actors are agents for responders (initiators come next)
      run = 0;
      while (run < sys->maxruns)
	{
	  if (!sys->runs[run].role->initiator)
	    {
	      Term actor;

	      actor = agentOfRun (sys, run);
	      if (!goodAgentType (actor))
		{
		  if (switches.output == PROOF)
		    {
		      indentPrint ();
		      eprintf ("Pruned because the actor ");
		      termPrint (actor);
		      eprintf (" of run %i is not of a compatible type.\n",
			       run);
		    }
		  return true;
		}
	    }
	  run++;
	}

      // Prune wrong agents type for initators
      if (!initiatorAgentsType (sys))
	{
	  if (switches.output == PROOF)
	    {
	      indentPrint ();
	      eprintf
		("Pruned: an initiator role does not have the correct type for one of its agents.\n");
	    }
	  return true;
	}

    }
  else
    {
      // Prune wrong agents type for runs
      if (!allAgentsType (sys))
	{
	  if (switches.output == PROOF)
	    {
	      indentPrint ();
	      eprintf
		("Pruned: some run does not have the correct type for one of its agents.\n");
	    }
	  return true;
	}
    }

  // Check if the actors of all other runs are not untrusted
  if (sys->untrusted != NULL)
    {
      int run;

      run = 1;
      while (run < sys->maxruns)
	{
	  if (sys->runs[run].protocol != INTRUDER)
	    {
	      if (sys->runs[run].rho != NULL)
		{
		  Term actor;

		  actor = agentOfRun (sys, run);
		  if (actor == NULL)
		    {
		      error ("Agent of run %i is NULL", run);
		    }
		  if (!isAgentTrusted (sys, actor))
		    {
		      if (switches.output == PROOF)
			{
			  indentPrint ();
			  eprintf
			    ("Pruned because the actor of run %i is untrusted.\n",
			     run);
			}
		      return true;
		    }
		}
	      else
		{
		  Protocol p;

		  globalError++;
		  eprintf ("error: Run %i: ", run);
		  role_name_print (run);
		  eprintf (" has an empty agents list.\n");
		  eprintf ("protocol->rolenames: ");
		  p = (Protocol) sys->runs[run].protocol;
		  termlistPrint (p->rolenames);
		  eprintf ("\n");
		  error ("Aborting.");
		  globalError--;
		  return true;
		}
	    }
	  run++;
	}
    }

  // Check for redundant patterns
  {
    if (!non_redundant ())
      {
	if (switches.output == PROOF)
	  {
	    indentPrint ();
	    eprintf ("Pruned because the pattern is redundant.\n");
	  }
	return true;
      }
  }

  // Check for violation of inequality constraints
  if (!inequalityConstraints (sys))
    {
      // Prune, because violated
      return true;
    }

  /*
   * Check for correct orderings involving local constants
   *
   * TODO: Clarify how this works with agent name variables in a non strict-typed setting.
   */
  if (!(switches.experimental & 8))
    {
      if (!correctLocalOrder (sys))
	{
	  if (switches.output == PROOF)
	    {
	      indentPrint ();
	      eprintf
		("Pruned because this does not have the correct local order.\n");
	    }
	  return true;
	}
    }

  /**
   * Check whether the bindings are valid
   */
  bl = sys->bindings;
  while (bl != NULL)
    {
      Binding b;

      b = bl->data;

      // Check for "Hidden" interm goals
      //! @todo in the future, this can be subsumed by adding TERM_Hidden to the hidelevel constructs
      if (termInTerm (b->term, TERM_Hidden))
	{
	  // Prune the state: we can never meet this
	  if (switches.output == PROOF)
	    {
	      indentPrint ();
	      eprintf ("Pruned because intruder can never construct ");
	      termPrint (b->term);
	      eprintf ("\n");
	    }
	  return true;
	}

      if (switches.experimental & 4)
	{
	  // Check for SK-type function occurrences
	  //!@todo Needs a LEMMA, although this seems to be quite straightforward to prove.
	  // The idea is that functions are never sent as a whole, but only used in applications.
	  //! @todo Subsumed by hidelevel lemma later
	  if (isTermFunctionName (b->term))
	    {
	      if (!inKnowledge (sys->know, b->term))
		{
		  // Not in initial knowledge of the intruder
		  if (switches.output == PROOF)
		    {
		      indentPrint ();
		      eprintf ("Pruned because the function ");
		      termPrint (b->term);
		      eprintf (" is not known initially to the intruder.\n");
		    }
		  return true;
		}
	    }
	}

      // Check for encryption levels
      /*
       * if (switches.match < 2
       *! @todo Doesn't work yet as desired for Tickets. Prove lemma first.
       */
      if (switches.experimental & 2)
	{
	  if (!hasTicketSubterm (b->term))
	    {
	      if (term_encryption_level (b->term) > max_encryption_level)
		{
		  // Prune: we do not need to construct such terms
		  if (switches.output == PROOF)
		    {
		      indentPrint ();
		      eprintf ("Pruned because the encryption level of ");
		      termPrint (b->term);
		      eprintf (" is too high.\n");
		    }
		  return true;
		}
	    }
	}

      // To be on the safe side, we currently limit the encryption level. 
      /**
       * This is valid *only* if there are no ticket-type variables.
       */
      if (term_encryption_level (b->term) > max_encryption_level)
	{
	  // Prune: we do not need to construct such terms
	  if (sys->hasUntypedVariable)
	    {
	      sys->current_claim->complete = false;
	    }
	  if (switches.output == PROOF)
	    {
	      indentPrint ();
	      eprintf ("Pruned because the encryption level of ");
	      termPrint (b->term);
	      eprintf (" is too high.\n");
	    }
	  return true;
	}

      /**
       * Prune on the basis of hidelevel lemma
       */
      if (hidelevelImpossible (sys, b->term))
	{
	  // Prune: we do not need to construct such terms
	  if (switches.output == PROOF)
	    {
	      indentPrint ();
	      eprintf ("Pruned because the hidelevel of ");
	      termPrint (b->term);
	      eprintf (" is impossible to satisfy.\n");
	    }
	  return true;
	}

      bl = bl->next;
    }

  /* check for singular roles */
  run = 0;
  while (run < sys->maxruns)
    {
      if (sys->runs[run].role->singular)
	{
	  // This is a singular role: it therefore should not occur later on again.
	  int run2;
	  Term rolename;

	  rolename = sys->runs[run].role->nameterm;
	  run2 = run + 1;
	  while (run2 < sys->maxruns)
	    {
	      Term rolename2;

	      rolename2 = sys->runs[run2].role->nameterm;
	      if (isTermEqual (rolename, rolename2))
		{
		  // This is not allowed: the singular role occurs twice in the semitrace.
		  // Thus we prune.
		  if (switches.output == PROOF)
		    {
		      indentPrint ();
		      eprintf ("Pruned because the singular role ");
		      termPrint (rolename);
		      eprintf (" occurs more than once in the semitrace.\n");
		    }
		  return true;
		}
	      run2++;
	    }
	}
      run++;
    }

  return false;
}