scyther/src/heuristic.c
2013-10-05 23:56:12 +01:00

520 lines
9.5 KiB
C

/*
* 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.
*/
/**
*
*@file heuristic.c
*
* Heuristics code for Arachne method
*
*/
#include <float.h>
#include <stdlib.h>
#include "binding.h"
#include "system.h"
#include "specialterm.h"
#include "switches.h"
#include "hidelevel.h"
#include "arachne.h"
#include "error.h"
#include "knowledge.h"
//! Trivial goal detection
/**
* Detect goals that we do not need to solve because they can be trivially solved.
*/
int
isTrivialGoal (const System sys, const Term t)
{
if (isTermVariable (t))
{
return true;
}
else
{
Term f;
f = getTermFunction (t);
if (f != NULL)
{
if (isKnowledgePublicFunction (sys->know, f))
{
return isTrivialGoal (sys, TermOp (t));
}
}
}
return false;
}
//! Check whether a binding (goal) is selectable
int
is_goal_selectable (const System sys, const Binding b)
{
if (b != NULL)
{
if ((!b->blocked) && (!b->done))
{
if (!isTrivialGoal (sys, b->term))
{
return true;
}
}
}
return false;
}
//! Count selectable goals
int
count_selectable_goals (const System sys)
{
List bl;
int n;
n = 0;
bl = sys->bindings;
while (bl != NULL)
{
Binding b;
b = (Binding) bl->data;
if (is_goal_selectable (sys, b))
{
n++;
}
bl = bl->next;
}
return n;
}
//! Return first selectable goal in the list
/**
* The return list entry is either NULL, or a selectable goal.
*/
List
first_selectable_goal (const System sys, List bl)
{
while (bl != NULL && !is_goal_selectable (sys, (Binding) bl->data))
{
bl = bl->next;
}
return bl;
}
//! Determine whether a term is an open nonce variable
/**
* Does not explore subterms
*/
int
isOpenNonceVar (Term t)
{
t = deVar (t);
if (realTermVariable (t))
{
return inTermlist (t->stype, TERM_Nonce);
}
else
{
return 0;
}
}
//! Count unique open variables in term
/**
*/
int
count_open_variables (const Term t)
{
Termlist tl;
int n;
tl = NULL;
termlistAddVariables (tl, t);
n = 0;
while (tl != NULL)
{
if (!inTermlist (tl->next, t))
{
if (isOpenNonceVar (t))
{
n = n + 1;
}
}
tl = tl->next;
}
termlistDelete (tl);
return n;
}
//! Athena-like factor
/**
* Lower is better (more nonce variables)
*/
float
term_noncevariables_level (const Term t)
{
int onv;
const int enough = 2;
onv = count_open_variables (t);
if (onv >= enough)
{
return 0;
}
else
{
return 1 - (onv / enough);
}
}
//! Determine weight based on hidelevel
float
weighHidelevel (const System sys, const Term t, const float massknow,
const float massprot)
{
switch (hidelevelFlag (sys, t))
{
case HLFLAG_NONE:
return 0;
case HLFLAG_KNOW:
return massknow;
case HLFLAG_PROT:
return massprot;
}
return 1;
}
//! newkeylevel (weighted)
int
newkeylevel (const int level)
{
// keylevel is from { -1,0,1 } where -1 means delay
if (level == 1)
return 0;
else
return 1;
}
//! count local constants
float
term_constcount (const System sys, Term t)
{
int n, total;
float ratio;
int countMe (Term t)
{
if (TermRunid (t) >= 0)
{
total++;
if (!isTermVariable (t))
{
n++;
}
}
return 1;
}
n = 0;
total = 0;
term_iterate_deVar (t, countMe, NULL, NULL, NULL);
if (total == 0)
{
ratio = 1;
}
else
{
ratio = ((total - n) / total);
}
return ratio;
}
//! determine lowest level at which sk/sk occurs, or -1 otherwise.
int
lowest_skk_level (Term t)
{
t = deVar (t);
if (t == NULL)
{
return -1;
}
if (isTermEqual (t, TERM_SK) || isTermEqual (t, TERM_K))
{
return 0;
}
if (realTermLeaf (t))
{
return -1;
}
if (realTermTuple (t))
{
int x, y;
x = lowest_skk_level (TermOp1 (t));
y = lowest_skk_level (TermOp2 (t));
if (x < 0)
return y;
if (y < 0)
return x;
if (y < x)
return y;
else
return x;
}
else
{
int x, y;
x = lowest_skk_level (TermOp (t));
y = lowest_skk_level (TermKey (t));
if (x < 0)
return y + 1;
if (y < 0)
return x + 1;
if (y < x)
return y + 1;
else
return x + 1;
}
}
//! count sk/k level depth
float
term_skk_level (const System sys, Term t)
{
int l;
l = lowest_skk_level (t);
if (l < 0)
{
return 1.0;
}
else
{
float res;
res = l / 4;
if (res > 1)
return 1;
else
return res;
}
}
//! Determine the weight of a given goal
/**
* 0 to ... (lower is better)
*
* --heuristic has two distint interpretations. If it is 0 or greater, it a
* selection mask. If it is smaller than 0, it is some special tactic.
*
* selection masks for --select-goal
* 1: constrain level of term
* 2: key or not
* 4: consequences determination
* 8: select also single variables (that are not role variables)
* 16: single variables are better
* 32: incorporate keylevel information
*
* special tactics for --select-goal
* -1: random goal selection
*
*/
float
computeGoalWeight (const System sys, const Binding b)
{
float w;
int smode;
Term t;
void erode (const float deltaw)
{
if (smode & 1)
{
w = w + deltaw;
}
smode = smode / 2;
}
// Total weight
w = 0;
// We will shift this mode variable
smode = switches.heuristic;
t = b->term;
// Determine buf_constrain levels
// Bit 0: 1 use hidelevel
erode (2 * weighHidelevel (sys, t, 0.5, 0.5));
// Bit 1: 2 key level (inverted)
erode (0.5 * (1 - b->level));
// Bit 2: 4 constrain level
erode (term_constrain_level (t));
// Bit 3: 8 nonce variables level (Cf. what I think is in Athena)
erode (term_noncevariables_level (t));
// Bit 4: 16 use hidelevel (normal)
erode (1 * weighHidelevel (sys, t, 0.5, 0.5));
// Bit 5: 32 use known nonces (Athena try 2)
erode (term_constcount (sys, t));
// Bit 6: 64 use hidelevel (but only single-weight)
erode (weighHidelevel (sys, t, 0.5, 0.5));
// Bit 7: 128 use hidelevel (quadruple-weight)
erode (4 * weighHidelevel (sys, t, 0.5, 0.5));
// Bit 8: 256 use known nonces (Athena try 2), half weight
erode (0.5 * term_constcount (sys, t));
// Bit 9: 512 do sk first (first sk, then sk occurrence level 1, etc)
erode (16 * term_skk_level (sys, t));
// Define legal range
if (smode > 0)
error ("--heuristic mode %i is illegal", switches.heuristic);
// Return
return w;
}
//! Goal selection
/**
* Selects the most constrained goal.
*
* Because the list starts with the newest terms, and we use <= (as opposed to <), we
* ensure that for goals with equal constraint levels, we select the oldest one.
*
*/
Binding
select_goal_masked (const System sys)
{
List bl;
Binding best;
float best_weight;
// Find the most constrained goal
if (switches.output == PROOF)
{
indentPrint ();
eprintf ("Listing open goals that might be chosen: ");
}
best_weight = FLT_MAX;
best = NULL;
bl = sys->bindings;
while (bl != NULL)
{
Binding b;
b = (Binding) bl->data;
// Only if not done and not blocked
if (is_goal_selectable (sys, b))
{
float w;
w = computeGoalWeight (sys, b);
// Spacing between output
if (switches.output == PROOF && best != NULL)
eprintf (", ");
// Better alternative?
if (w <= best_weight)
{
best_weight = w;
best = b;
if (switches.output == PROOF)
eprintf ("*");
}
if (switches.output == PROOF)
{
termPrint (b->term);
eprintf ("<%.2f>", w);
}
}
bl = bl->next;
}
if (switches.output == PROOF)
{
if (best == NULL)
eprintf ("none");
eprintf ("\n");
}
return best;
}
//! Goal selection special case -1: random
/**
* Simply picks an open goal randomly. Has to be careful to skip singular stuff etc.
*/
Binding
select_goal_random (const System sys)
{
int n;
n = count_selectable_goals (sys);
if (n > 0)
{
int choice;
List bl;
// Choose a random goal between 0 and n
choice = rand () % n;
// Fetch it
bl = sys->bindings;
while (choice >= 0)
{
bl = first_selectable_goal (sys, bl);
if (bl == NULL)
{
error ("Random chooser selected a NULL goal.");
}
choice--;
}
return (Binding) bl->data;
}
else
{
return (Binding) NULL;
}
}
//! Goal selection function, generic
Binding
select_goal (const System sys)
{
if (switches.heuristic >= 0)
{
// Masked
return select_goal_masked (sys);
}
else
{
// Special cases
switch (switches.heuristic)
{
case -1:
return select_goal_random (sys);
}
error ("Unknown value (<0) for --goal-select.");
}
return (Binding) NULL;
}