- Added gui.
This commit is contained in:
parent
460c087cf2
commit
e1ddf0668b
52
gui/Attack.py
Normal file
52
gui/Attack.py
Normal file
@ -0,0 +1,52 @@
|
||||
#
|
||||
# Attack
|
||||
#
|
||||
|
||||
import Trace
|
||||
import Term
|
||||
#import Classification
|
||||
from Misc import *
|
||||
|
||||
class Attack(object):
|
||||
def __init__(self):
|
||||
self.broken = []
|
||||
self.match = None
|
||||
self.initialKnowledge = []
|
||||
self.inverseKeys = []
|
||||
self.protocol = None
|
||||
self.semiTrace = Trace.SemiTrace()
|
||||
self.variables = []
|
||||
self.protocoldescr = {}
|
||||
self.id = None
|
||||
self.knowledge = None
|
||||
self.untrusted = []
|
||||
self.typeflaws = False
|
||||
self.commandline = ''
|
||||
self.scytherDot = None
|
||||
self.claim = None # refers to parent claim
|
||||
|
||||
def getInvolvedAgents(self):
|
||||
result = []
|
||||
for run in self.semiTrace.runs:
|
||||
for agent in run.roleAgents.values():
|
||||
result.append(agent)
|
||||
return uniq(result)
|
||||
|
||||
def buildKnowledge(self):
|
||||
if not self.knowledge:
|
||||
self.knowledge = Term.Knowledge(self)
|
||||
self.knowledge.buildKnowledge()
|
||||
|
||||
def getPrecedingLabelSet(self,event):
|
||||
return self.protocoldescr[str(event.label[0])].getPrecedingLabelSet(event.label)
|
||||
|
||||
def getPrecedingRoleSet(self,event):
|
||||
return self.protocoldescr[str(event.label[0])].getPrecedingRoleSet(event.label)
|
||||
|
||||
#def classify(self):
|
||||
# classification = Classification.Classification(self)
|
||||
# classification.classifyClaims()
|
||||
# classification.classifyInitiations()
|
||||
# classification.classifyComplexity()
|
||||
# classification.classifyTypeflaws()
|
||||
# return classification
|
41
gui/Attackimage.py
Normal file
41
gui/Attackimage.py
Normal file
@ -0,0 +1,41 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
""" Import externals """
|
||||
import os
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
""" Import scyther-gui components """
|
||||
import Tempfile
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class AttackImage:
|
||||
def __init__(self,dotdata):
|
||||
self.dotdata = dotdata
|
||||
self.png = ""
|
||||
|
||||
self.MakeImage()
|
||||
|
||||
def MakeImage(self):
|
||||
""" Sets png """
|
||||
|
||||
(fd,fpname) = Tempfile.tempcleaned(".dot")
|
||||
fp = os.fdopen(fd, "w")
|
||||
fp.write(self.dotdata)
|
||||
fp.close()
|
||||
|
||||
(fd2,fpname2) = Tempfile.tempcleaned(".png")
|
||||
os.system("dot %s -Tpng >%s" % (fpname, fpname2))
|
||||
self.png = fpname2
|
||||
|
||||
Tempfile.tempcleanearly((fd,fpname))
|
||||
|
||||
def GetImage(self):
|
||||
|
||||
return self.png
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
169
gui/Attackwindow.py
Normal file
169
gui/Attackwindow.py
Normal file
@ -0,0 +1,169 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
""" Import externals """
|
||||
import wx
|
||||
import time
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
""" Import scyther-gui components """
|
||||
import Icon
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class AttackDisplay(wx.ScrolledWindow):
|
||||
def __init__(self, daddy, parent, claim,attackid):
|
||||
|
||||
self.win = daddy
|
||||
|
||||
wx.ScrolledWindow.__init__(self,parent,id=-1)
|
||||
# Wait for the attack to be computed
|
||||
while attackid >= len(claim.attacks):
|
||||
time.sleep(1)
|
||||
|
||||
self.Bind(wx.EVT_SIZE, self.OnSize)
|
||||
self.Image = wx.StaticBitmap(self, -1, wx.EmptyBitmap(1,1))
|
||||
self.box = wx.BoxSizer(wx.VERTICAL)
|
||||
self.box.Add(self.Image,1,wx.ALIGN_CENTER)
|
||||
self.hbox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
self.hbox.Add(self.box,1,wx.ALIGN_CENTER)
|
||||
self.SetSizer(self.hbox)
|
||||
|
||||
filename = claim.attacks[attackid].GetImage()
|
||||
self.original = wx.Image(filename,wx.BITMAP_TYPE_PNG)
|
||||
|
||||
|
||||
self.update()
|
||||
|
||||
# TODO self.Bind(wxSizeEvent
|
||||
|
||||
def OnSize(self,event):
|
||||
self.update()
|
||||
event.Skip()
|
||||
|
||||
def update(self):
|
||||
|
||||
self.SetScrollbars(0,0,0,0,0,0)
|
||||
|
||||
bmp = self.original
|
||||
if self.win.fit:
|
||||
W = bmp.GetWidth()
|
||||
H = bmp.GetHeight()
|
||||
(sw,sh) = self.win.GetClientSizeTuple()
|
||||
|
||||
if W > sw:
|
||||
# correct width
|
||||
factor = float(sw) / W
|
||||
W = sw
|
||||
H = H * factor
|
||||
if H > sh:
|
||||
# correct height
|
||||
factor = float(sh) / H
|
||||
H = sh
|
||||
W = W * factor
|
||||
|
||||
|
||||
bmp = self.original.Scale(W,H)
|
||||
|
||||
self.Image.SetBitmap(wx.BitmapFromImage(bmp))
|
||||
#self.box.SetItemMinSize(self.Image.GetContainingSizer())
|
||||
self.box.Layout()
|
||||
|
||||
# wx.StaticBitmap(self, -1, bmp, (0, 0), (bmp.GetWidth(), bmp.GetHeight()))
|
||||
step = 20
|
||||
xn = int(bmp.GetWidth() / step) + 1
|
||||
yn = int(bmp.GetHeight() / step) + 1
|
||||
self.SetScrollbars(step,step,xn,yn,0,0)
|
||||
|
||||
self.Refresh()
|
||||
|
||||
|
||||
class AttackWindow(wx.Frame):
|
||||
def __init__(self,cl):
|
||||
super(AttackWindow, self).__init__(None, size=(400,800))
|
||||
self.claim = cl
|
||||
self.fit = False
|
||||
self.CreateInteriorWindowComponents()
|
||||
self.CreateExteriorWindowComponents()
|
||||
|
||||
Icon.ScytherIcon(self)
|
||||
self.SetTitle()
|
||||
|
||||
def SetTitle(self):
|
||||
|
||||
if self.claim.attackcount == 1:
|
||||
tstr = "Attack"
|
||||
else:
|
||||
tstr = "Attacks"
|
||||
|
||||
tstr += " for claim %s" % str(self.claim)
|
||||
super(AttackWindow, self).SetTitle(tstr)
|
||||
|
||||
|
||||
def CreateInteriorWindowComponents(self):
|
||||
''' Create "interior" window components. In this case it is the
|
||||
attack picture. '''
|
||||
|
||||
self.displays=[]
|
||||
if self.claim.attackcount <= 1:
|
||||
# Just a single window
|
||||
self.tabs = None
|
||||
self.displays.append(AttackDisplay(self,self,self.claim,0))
|
||||
else:
|
||||
# Multiple tabs
|
||||
self.tabs = wx.Notebook(self,-1)
|
||||
for i in range(0,self.claim.attackcount):
|
||||
disp = AttackDisplay(self,self.tabs,self.claim,i)
|
||||
classname = "Class %i" % ((i+1))
|
||||
self.tabs.AddPage(disp, classname)
|
||||
self.displays.append(disp)
|
||||
|
||||
self.Show(1)
|
||||
|
||||
|
||||
def CreateExteriorWindowComponents(self):
|
||||
''' Create "exterior" window components, such as menu and status
|
||||
bars '''
|
||||
self.CreateStatusBar()
|
||||
self.SetupToolBar()
|
||||
|
||||
def SetupToolBar(self):
|
||||
|
||||
tb = self.CreateToolBar(wx.TB_HORIZONTAL
|
||||
| wx.NO_BORDER
|
||||
| wx.TB_FLAT
|
||||
| wx.TB_TEXT
|
||||
)
|
||||
|
||||
# Add fit button
|
||||
bmp = wx.ArtProvider_GetBitmap(wx.ART_MISSING_IMAGE,wx.ART_TOOLBAR,(20,20))
|
||||
if not bmp.Ok():
|
||||
bmp = wx.EmptyBitmap(20,20)
|
||||
tb.AddCheckTool(wx.ID_ZOOM_FIT, bmp, bmp, 'Toggle zoom', 'Toggle zoom level')
|
||||
self.Bind(wx.EVT_TOOL, self.OnFit, id=wx.ID_ZOOM_FIT)
|
||||
|
||||
# And shortcut
|
||||
aTable = wx.AcceleratorTable([
|
||||
(wx.ACCEL_NORMAL, ord('Z'), wx.ID_ZOOM_FIT)
|
||||
])
|
||||
self.SetAcceleratorTable(aTable)
|
||||
|
||||
def update(self):
|
||||
for t in self.displays:
|
||||
t.update()
|
||||
|
||||
def OnFit(self,event):
|
||||
|
||||
if self.fit:
|
||||
self.fit = False
|
||||
else:
|
||||
self.fit = True
|
||||
self.update()
|
||||
|
||||
def OnRealSize(self):
|
||||
|
||||
self.fit = False
|
||||
self.update()
|
||||
|
66
gui/Claim.py
Normal file
66
gui/Claim.py
Normal file
@ -0,0 +1,66 @@
|
||||
#
|
||||
# Claim
|
||||
#
|
||||
|
||||
import Term
|
||||
|
||||
class Claim(object):
|
||||
def __init__(self):
|
||||
self.claimtype = None
|
||||
self.label = None
|
||||
self.protocol = None
|
||||
self.role = None
|
||||
self.parameter = None
|
||||
self.failed = 0
|
||||
self.count = 0
|
||||
self.states = 0
|
||||
self.complete = False
|
||||
self.timebound = False
|
||||
self.attacks = []
|
||||
self.state = False # if true, it is a state, not an attack
|
||||
|
||||
# derived info
|
||||
self.foundstates = False
|
||||
self.foundproof = False
|
||||
|
||||
def analyze(self):
|
||||
if str(self.claimtype) == 'Reachable':
|
||||
self.state = True
|
||||
if self.failed > 0:
|
||||
self.foundstates = True
|
||||
if self.complete:
|
||||
self.foundproof = True
|
||||
|
||||
def stateName(self,count=1):
|
||||
if self.state:
|
||||
s = "state"
|
||||
else:
|
||||
s = "attack"
|
||||
if count != 1:
|
||||
s += "s"
|
||||
return s
|
||||
|
||||
def __str__(self):
|
||||
s = "claim "
|
||||
s+= " " + str(self.protocol)
|
||||
s+= " " + str(self.role)
|
||||
|
||||
# We need the rightmost thingy here
|
||||
label = self.label
|
||||
while isinstance(label,Term.TermTuple):
|
||||
label = label[1]
|
||||
|
||||
s+= " " + str(label)
|
||||
s+= " " + str(self.claimtype)
|
||||
if self.parameter:
|
||||
s+= " " + str(self.parameter)
|
||||
|
||||
# determine status
|
||||
s+= " : %i " % (self.failed)
|
||||
s+= self.stateName(self.failed)
|
||||
if self.complete:
|
||||
s+= " (complete)"
|
||||
|
||||
return s
|
||||
|
||||
|
23
gui/Icon.py
Normal file
23
gui/Icon.py
Normal file
@ -0,0 +1,23 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
""" Import externals """
|
||||
import wx
|
||||
import os.path
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
""" Import scyther-gui components """
|
||||
import Misc
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
def ScytherIcon(window):
|
||||
""" Set a nice Scyther icon """
|
||||
iconfile = Misc.mypath("scyther-gui-32.ico")
|
||||
if os.path.isfile(iconfile):
|
||||
icon = wx.Icon(iconfile,wx.BITMAP_TYPE_ICO)
|
||||
window.SetIcon(icon)
|
||||
|
||||
|
399
gui/Mainwindow.py
Normal file
399
gui/Mainwindow.py
Normal file
@ -0,0 +1,399 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
""" Import externals """
|
||||
import wx
|
||||
import os.path
|
||||
import sys
|
||||
import wx.lib.mixins.listctrl as listmix
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
""" Import scyther-gui components """
|
||||
import Preference
|
||||
import Attackwindow
|
||||
import Scytherthread
|
||||
import Icon
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
""" Some constants """
|
||||
ID_VERIFY = 100
|
||||
ID_AUTOVERIFY = 101
|
||||
ID_STATESPACE = 102
|
||||
ID_CHECK = 103
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class MainWindow(wx.Frame):
|
||||
|
||||
def __init__(self, filename=''):
|
||||
super(MainWindow, self).__init__(None, size=(600,800))
|
||||
self.dirname = '.'
|
||||
|
||||
if filename != '' and os.path.isfile(filename):
|
||||
self.filename = filename
|
||||
self.load = True
|
||||
else:
|
||||
self.filename = 'noname.spdl'
|
||||
self.load = False
|
||||
|
||||
Icon.ScytherIcon(self)
|
||||
|
||||
self.CreateInteriorWindowComponents()
|
||||
self.CreateExteriorWindowComponents()
|
||||
|
||||
aTable = wx.AcceleratorTable([
|
||||
#(wx.ACCEL_ALT, ord('X'), exitID),
|
||||
(wx.ACCEL_CTRL, ord('W'), wx.ID_EXIT),
|
||||
(wx.ACCEL_NORMAL, wx.WXK_F1,
|
||||
ID_VERIFY),
|
||||
(wx.ACCEL_NORMAL, wx.WXK_F2,
|
||||
ID_STATESPACE),
|
||||
(wx.ACCEL_NORMAL, wx.WXK_F5,
|
||||
ID_CHECK),
|
||||
(wx.ACCEL_NORMAL, wx.WXK_F6,
|
||||
ID_AUTOVERIFY),
|
||||
])
|
||||
self.SetAcceleratorTable(aTable)
|
||||
|
||||
self.claimlist = []
|
||||
self.pnglist = []
|
||||
|
||||
#self.SetTitle(self.title)
|
||||
|
||||
def CreateInteriorWindowComponents(self):
|
||||
''' Create "interior" window components. In this case it is just a
|
||||
simple multiline text control. '''
|
||||
|
||||
self.splitter = wx.SplitterWindow(self,-1)
|
||||
self.splitter.daddy = self
|
||||
|
||||
# Top: input
|
||||
self.top = wx.Notebook(self.splitter,-1)
|
||||
self.control = wx.TextCtrl(self.top, style=wx.TE_MULTILINE)
|
||||
if self.load:
|
||||
textfile = open(os.path.join(self.dirname, self.filename), 'r')
|
||||
self.control.SetValue(textfile.read())
|
||||
os.chdir(self.dirname)
|
||||
textfile.close()
|
||||
self.top.AddPage(self.control,"Protocol")
|
||||
self.settings = SettingsWindow(self.top,self)
|
||||
self.top.AddPage(self.settings,"Settings")
|
||||
|
||||
# Bottom: output
|
||||
self.bottom = wx.Notebook(self.splitter,-1)
|
||||
self.report = SummaryWindow(self.bottom,self)
|
||||
self.bottom.AddPage(self.report,"Claim summary")
|
||||
self.errors = ErrorWindow(self.bottom,self)
|
||||
self.bottom.AddPage(self.errors,"Detailed output")
|
||||
|
||||
#self.report.SetValue("Welcome.")
|
||||
|
||||
# Combine
|
||||
self.splitter.SetMinimumPaneSize(20)
|
||||
self.splitter.SplitHorizontally(self.top, self.bottom)
|
||||
self.splitter.SetSashPosition(510)
|
||||
self.Show(1)
|
||||
|
||||
|
||||
def CreateExteriorWindowComponents(self):
|
||||
''' Create "exterior" window components, such as menu and status
|
||||
bar. '''
|
||||
self.CreateMenus()
|
||||
self.CreateStatusBar()
|
||||
self.SetupToolBar()
|
||||
self.SetTitle()
|
||||
|
||||
def SetupToolBar(self):
|
||||
|
||||
tb = self.CreateToolBar(wx.TB_HORIZONTAL
|
||||
| wx.NO_BORDER
|
||||
| wx.TB_FLAT
|
||||
| wx.TB_TEXT
|
||||
)
|
||||
|
||||
bmp = wx.ArtProvider_GetBitmap(wx.ART_EXECUTABLE_FILE,wx.ART_TOOLBAR,(20,20))
|
||||
if not bmp.Ok():
|
||||
bmp = wx.EmptyBitmap(20,20)
|
||||
tb.AddSimpleTool(ID_VERIFY, bmp,"Verify","Verify claims")
|
||||
self.Bind(wx.EVT_TOOL, self.OnVerify, id=ID_VERIFY)
|
||||
tb.AddSimpleTool(ID_STATESPACE, bmp,"Statespace","Generate statespace for all roles")
|
||||
self.Bind(wx.EVT_TOOL, self.OnStatespace, id=ID_STATESPACE)
|
||||
tb.AddSeparator()
|
||||
|
||||
tb.AddSimpleTool(ID_CHECK, bmp,"Check","Check protocol")
|
||||
self.Bind(wx.EVT_TOOL, self.OnCheck, id=ID_CHECK)
|
||||
tb.AddSimpleTool(ID_AUTOVERIFY, bmp,"Default claims","Verify default claims")
|
||||
self.Bind(wx.EVT_TOOL, self.OnAutoVerify, id=ID_AUTOVERIFY)
|
||||
|
||||
def CreateMenu(self, bar, name, list):
|
||||
|
||||
fileMenu = wx.Menu()
|
||||
for id, label, helpText, handler in list:
|
||||
if id == None:
|
||||
fileMenu.AppendSeparator()
|
||||
else:
|
||||
item = fileMenu.Append(id, label, helpText)
|
||||
self.Bind(wx.EVT_MENU, handler, item)
|
||||
bar.Append(fileMenu, name) # Add the fileMenu to the MenuBar
|
||||
|
||||
|
||||
def CreateMenus(self):
|
||||
menuBar = wx.MenuBar()
|
||||
self.CreateMenu(menuBar, '&File', [
|
||||
(wx.ID_OPEN, '&Open', 'Open a new file', self.OnOpen),
|
||||
(wx.ID_SAVE, '&Save', 'Save the current file', self.OnSave),
|
||||
(wx.ID_SAVEAS, 'Save &As', 'Save the file under a different name',
|
||||
self.OnSaveAs),
|
||||
(None, None, None, None),
|
||||
(wx.ID_EXIT, 'E&xit\tCTRL-W', 'Terminate the program',
|
||||
self.OnExit)])
|
||||
self.CreateMenu(menuBar, '&Verify',
|
||||
[(ID_VERIFY, '&Verify protocol\tF1','Verify the protocol in the buffer using Scyther',
|
||||
self.OnVerify) ,
|
||||
(ID_STATESPACE, 'Generate &statespace\tF2','TODO' ,
|
||||
self.OnAutoVerify) ,
|
||||
(None, None, None, None),
|
||||
(ID_CHECK, '&Check protocol\tF5','TODO',
|
||||
self.OnStatespace) ,
|
||||
(ID_AUTOVERIFY, 'Verify &automatic claims\tF6','TODO',
|
||||
self.OnCheck)
|
||||
])
|
||||
self.CreateMenu(menuBar, '&Help',
|
||||
[(wx.ID_ABOUT, '&About', 'Information about this program',
|
||||
self.OnAbout) ])
|
||||
self.SetMenuBar(menuBar) # Add the menuBar to the Frame
|
||||
|
||||
|
||||
def SetTitle(self):
|
||||
# MainWindow.SetTitle overrides wx.Frame.SetTitle, so we have to
|
||||
# call it using super:
|
||||
super(MainWindow, self).SetTitle('Scyther-gui: %s'%self.filename)
|
||||
|
||||
|
||||
# Helper methods:
|
||||
|
||||
def defaultFileDialogOptions(self):
|
||||
''' Return a dictionary with file dialog options that can be
|
||||
used in both the save file dialog as well as in the open
|
||||
file dialog. '''
|
||||
return dict(message='Choose a file', defaultDir=self.dirname,
|
||||
wildcard='*.spdl')
|
||||
|
||||
def askUserForFilename(self, **dialogOptions):
|
||||
dialog = wx.FileDialog(self, **dialogOptions)
|
||||
if dialog.ShowModal() == wx.ID_OK:
|
||||
userProvidedFilename = True
|
||||
self.filename = dialog.GetFilename()
|
||||
self.dirname = dialog.GetDirectory()
|
||||
self.SetTitle() # Update the window title with the new filename
|
||||
else:
|
||||
userProvidedFilename = False
|
||||
dialog.Destroy()
|
||||
return userProvidedFilename
|
||||
|
||||
# Event handlers:
|
||||
|
||||
def OnAbout(self, event):
|
||||
msg = "Scyther GUI\n\nScyther and Scyther GUI\ndeveloped by Cas Cremers"
|
||||
dialog = wx.MessageDialog(self,msg, 'About scyther-gui', wx.OK)
|
||||
dialog.ShowModal()
|
||||
dialog.Destroy()
|
||||
|
||||
def OnExit(self, event):
|
||||
self.Close() # Close the main window.
|
||||
|
||||
def OnSave(self, event):
|
||||
textfile = open(os.path.join(self.dirname, self.filename), 'w')
|
||||
textfile.write(self.control.GetValue())
|
||||
textfile.close()
|
||||
|
||||
def OnOpen(self, event):
|
||||
if self.askUserForFilename(style=wx.OPEN,
|
||||
**self.defaultFileDialogOptions()):
|
||||
textfile = open(os.path.join(self.dirname, self.filename), 'r')
|
||||
self.control.SetValue(textfile.read())
|
||||
textfile.close()
|
||||
|
||||
def OnSaveAs(self, event):
|
||||
if self.askUserForFilename(defaultFile=self.filename, style=wx.SAVE,
|
||||
**self.defaultFileDialogOptions()):
|
||||
self.OnSave(event)
|
||||
os.chdir(self.dirname)
|
||||
|
||||
def RunScyther(self, mode):
|
||||
Scytherthread.RunScyther(self,mode)
|
||||
|
||||
def OnVerify(self, event):
|
||||
self.RunScyther("verify")
|
||||
|
||||
def OnAutoVerify(self, event):
|
||||
self.RunScyther("autoverify")
|
||||
|
||||
def OnStatespace(self, event):
|
||||
self.RunScyther("statespace")
|
||||
|
||||
def OnCheck(self, event):
|
||||
self.RunScyther("check")
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class SettingsWindow(wx.Panel):
|
||||
|
||||
def __init__(self,parent,daddy):
|
||||
wx.Panel.__init__(self,parent,-1)
|
||||
|
||||
self.win = daddy
|
||||
|
||||
|
||||
# Bound on the number of runs
|
||||
self.maxruns = int(Preference.get('maxruns','5'))
|
||||
r1 = wx.StaticText(self,-1,"Maximum number of runs (0 disables bound)")
|
||||
l1 = wx.SpinCtrl(self, -1, "",size=(150,-1))
|
||||
l1.SetRange(0,100)
|
||||
l1.SetValue(self.maxruns)
|
||||
self.Bind(wx.EVT_SPINCTRL,self.EvtRuns,l1)
|
||||
|
||||
# Matchin options
|
||||
self.match = int(Preference.get('match','0'))
|
||||
claimoptions = ['typed matching','find basic type flaws','find all type flaws']
|
||||
r2 = wx.StaticText(self,-1,"Matching type")
|
||||
l2 = wx.RadioBox(self, -1, "",
|
||||
wx.DefaultPosition,wx.DefaultSize,claimoptions,1,wx.RA_SPECIFY_COLS)
|
||||
l2.SetSelection(self.match)
|
||||
self.Bind(wx.EVT_RADIOBOX,self.EvtMatch,l2)
|
||||
|
||||
### MISC expert stuff
|
||||
|
||||
self.misc = Preference.get('scytheroptions','')
|
||||
r10 = wx.StaticText(self,-1,"Additional parameters for the Scyther tool")
|
||||
l10 = wx.TextCtrl(self,-1,self.misc,size=(150,-1))
|
||||
self.Bind(wx.EVT_TEXT,self.EvtMisc,l10)
|
||||
|
||||
# Combine
|
||||
space = 10
|
||||
sizer = wx.FlexGridSizer(cols=3, hgap=space,vgap=space)
|
||||
sizer.AddMany([ l1,r1, (0,0),
|
||||
l2,r2, (0,0),
|
||||
l10,r10, (0,0),
|
||||
])
|
||||
self.SetSizer(sizer)
|
||||
self.SetAutoLayout(True)
|
||||
|
||||
def EvtMatch(self,evt):
|
||||
self.match = evt.GetInt()
|
||||
|
||||
def EvtRuns(self,evt):
|
||||
self.maxruns = evt.GetInt()
|
||||
|
||||
def EvtMisc(self,evt):
|
||||
self.misc = evt.GetString()
|
||||
|
||||
def ScytherArguments(self):
|
||||
""" Note: constructed strings should have a space at the end to
|
||||
correctly separate the options.
|
||||
"""
|
||||
|
||||
tstr = ""
|
||||
|
||||
# Number of runs
|
||||
tstr += "--max-runs=%s " % (str(self.maxruns))
|
||||
# Matching type
|
||||
tstr += "--match=%s " % (str(self.match))
|
||||
|
||||
# Verification type
|
||||
if self.mode == "check":
|
||||
tstr += "--check "
|
||||
if self.mode == "autoverify":
|
||||
tstr += "--auto-claims "
|
||||
elif self.mode == "statespace":
|
||||
tstr += "--state-space "
|
||||
|
||||
# Anything else?
|
||||
if self.misc != "":
|
||||
tstr += " " + self.misc + " "
|
||||
|
||||
return tstr
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class SummaryWindow(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
|
||||
|
||||
def __init__(self,parent,daddy):
|
||||
wx.ListCtrl.__init__(self,parent,-1, style=wx.LC_REPORT
|
||||
| wx.BORDER_NONE
|
||||
| wx.LC_SORT_ASCENDING
|
||||
| wx.LC_SINGLE_SEL
|
||||
)
|
||||
listmix.ListCtrlAutoWidthMixin.__init__(self)
|
||||
|
||||
self.win = daddy
|
||||
self.currentItem = -1
|
||||
|
||||
self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
|
||||
|
||||
self.InsertColumn(0, "Protocol")
|
||||
self.InsertColumn(1, "Role")
|
||||
self.InsertColumn(2, "Claim")
|
||||
self.InsertColumn(3, "Label")
|
||||
self.InsertColumn(4, "Parameters")
|
||||
self.InsertColumn(5, "Status")
|
||||
self.InsertColumn(6, "Attacks")
|
||||
self.InsertColumn(7, "Comments")
|
||||
|
||||
def update(self):
|
||||
self.DeleteAllItems()
|
||||
self.claimlist = self.win.claimlist
|
||||
for key in range(0,len(self.claimlist)):
|
||||
cl = self.claimlist[key]
|
||||
index = self.InsertStringItem(sys.maxint,cl.protocol)
|
||||
self.SetStringItem(index,1,cl.role)
|
||||
self.SetStringItem(index,2,cl.claim)
|
||||
self.SetStringItem(index,3,cl.label)
|
||||
self.SetStringItem(index,4,cl.param)
|
||||
self.SetStringItem(index,5,cl.status)
|
||||
self.SetStringItem(index,6,str(cl.attackcount))
|
||||
self.SetStringItem(index,7,cl.comments)
|
||||
self.SetItemData(index,key)
|
||||
|
||||
if cl.status == "Fail":
|
||||
# Failed :(
|
||||
item = self.GetItem(key)
|
||||
item.SetTextColour(wx.RED)
|
||||
self.SetItem(item)
|
||||
else:
|
||||
# Okay! But with bound?
|
||||
if cl.comments.find("bounds") == -1:
|
||||
# No bounds, great :)
|
||||
item = self.GetItem(key)
|
||||
item.SetTextColour(wx.GREEN)
|
||||
self.SetItem(item)
|
||||
#for i in range(0,7):
|
||||
# self.SetColumnWidth(i,wx.LIST_AUTOSIZE)
|
||||
|
||||
def OnItemSelected(self, event):
|
||||
self.currentItem = event.m_itemIndex
|
||||
cl = self.claimlist[self.currentItem]
|
||||
if cl.attackcount > 0:
|
||||
display = Attackwindow.AttackWindow(cl)
|
||||
display.Show(1)
|
||||
self.Refresh()
|
||||
event.Skip()
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class ErrorWindow(wx.TextCtrl):
|
||||
|
||||
def __init__(self,parent,daddy):
|
||||
wx.TextCtrl.__init__(self,parent,-1, style=wx.TE_MULTILINE)
|
||||
self.win = daddy
|
||||
|
||||
def update(self,summary):
|
||||
self.SetValue(summary)
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
45
gui/Misc.py
Normal file
45
gui/Misc.py
Normal file
@ -0,0 +1,45 @@
|
||||
#
|
||||
# Misc.py
|
||||
# Various helper functions
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
""" Import externals """
|
||||
import os.path
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
def confirm(question):
|
||||
answer = ''
|
||||
while answer not in ('y','n'):
|
||||
print question,
|
||||
answer = raw_input().lower()
|
||||
return answer == 'y'
|
||||
|
||||
def exists(func,list):
|
||||
return len(filter(func,list)) > 0
|
||||
|
||||
def forall(func,list):
|
||||
return len(filter(func,list)) == len(list)
|
||||
|
||||
def uniq(li):
|
||||
result = []
|
||||
for elem in li:
|
||||
if (not elem in result):
|
||||
result.append(elem)
|
||||
return result
|
||||
|
||||
# Return a sorted copy of a list
|
||||
def sorted(li):
|
||||
result = li[:]
|
||||
result.sort()
|
||||
return result
|
||||
|
||||
|
||||
# path
|
||||
def mypath(file):
|
||||
""" Construct a file path relative to the scyther-gui main directory
|
||||
"""
|
||||
basedir = os.path.dirname(__file__)
|
||||
return os.path.join(basedir,file)
|
||||
|
132
gui/Preference.py
Normal file
132
gui/Preference.py
Normal file
@ -0,0 +1,132 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
Preferences window and logic for saving and loading such things.
|
||||
Thus, some default things can be set here.
|
||||
|
||||
init loads stuff
|
||||
save save the settings after some changes
|
||||
set(k,v)
|
||||
get(k)
|
||||
|
||||
Currently used:
|
||||
|
||||
match
|
||||
maxruns
|
||||
scyther
|
||||
scytheroptions
|
||||
"""
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
""" Import externals """
|
||||
|
||||
import wx
|
||||
import os.path
|
||||
from time import localtime,strftime
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
""" Import scyther-gui components """
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
""" Globals """
|
||||
|
||||
""" Locations of preferences. The last one is supposedly writable. """
|
||||
prefname = "scythergui-config"
|
||||
preflocs = ['/usr/local/lib/scyther','~/.scyther']
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class Preferences(dict):
|
||||
|
||||
def parse(self,line):
|
||||
line = line.strip()
|
||||
|
||||
""" Skip comments """
|
||||
if not line.startswith("#"):
|
||||
split = line.find("=")
|
||||
if split != -1:
|
||||
key = line[:split].strip()
|
||||
data = line[(split+1):]
|
||||
self[key] = data.decode("string_escape")
|
||||
print "Read %s=%s" % (key,self[key])
|
||||
|
||||
def load(self,file=""):
|
||||
if file == None:
|
||||
self["test1"] = "Dit is met een ' en een \", en dan\nde eerste dinges"
|
||||
self["test2"] = "En dit de tweede"
|
||||
elif file == "":
|
||||
"""
|
||||
Test default locations
|
||||
"""
|
||||
for f in preflocs:
|
||||
self.load(os.path.join(os.path.expanduser(f),prefname))
|
||||
|
||||
else:
|
||||
"""
|
||||
Read this file
|
||||
"""
|
||||
if os.path.isfile(file):
|
||||
fp = open(file,"r")
|
||||
for l in fp.readlines():
|
||||
self.parse(l)
|
||||
fp.close()
|
||||
|
||||
def show(self):
|
||||
print "Preferences:"
|
||||
for k in self.keys():
|
||||
print "%s=%s" % (k, self[k])
|
||||
|
||||
def save(self):
|
||||
|
||||
print "Saving preferences"
|
||||
prefpath = os.path.expanduser(preflocs[-1])
|
||||
if not os.access(prefpath,os.W_OK):
|
||||
os.makedirs(prefpath)
|
||||
savename = os.path.join(prefpath,prefname)
|
||||
fp = open(savename,"w")
|
||||
|
||||
fp.write("# Scyther-gui configuration file.\n#\n")
|
||||
date = strftime("%c",localtime())
|
||||
fp.write("# Last written on %s\n" % (date))
|
||||
fp.write("# Do not edit - any changes will be overwritten by Scyther-gui\n\n")
|
||||
|
||||
l = list(self.keys())
|
||||
l.sort()
|
||||
for k in l:
|
||||
fp.write("%s=%s\n" % (k, self[k].encode("string_escape")))
|
||||
|
||||
fp.close()
|
||||
|
||||
def init():
|
||||
"""
|
||||
Load the preferences from a file, if possible
|
||||
"""
|
||||
global prefs
|
||||
|
||||
prefs = Preferences()
|
||||
prefs.load("")
|
||||
|
||||
|
||||
def get(key,alt=None):
|
||||
global prefs
|
||||
|
||||
if prefs.has_key(key):
|
||||
return prefs[key]
|
||||
else:
|
||||
return alt
|
||||
|
||||
def set(key,value):
|
||||
global prefs
|
||||
|
||||
prefs[key]=value
|
||||
return
|
||||
|
||||
def save():
|
||||
global prefs
|
||||
|
||||
prefs.save()
|
||||
|
||||
|
112
gui/Scyther.py
Executable file
112
gui/Scyther.py
Executable file
@ -0,0 +1,112 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Scyther interface
|
||||
#
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
""" Import externals """
|
||||
import os
|
||||
import sys
|
||||
import StringIO
|
||||
import tempfile
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
""" Import scyther components """
|
||||
import XMLReader
|
||||
from Misc import *
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class Scyther(object):
|
||||
def __init__ ( self):
|
||||
self.program = "scyther"
|
||||
self.options = ""
|
||||
self.spdl = None
|
||||
self.claims = None
|
||||
|
||||
def setInput(self,spdl):
|
||||
self.spdl = spdl
|
||||
|
||||
def setFile(self,filename):
|
||||
self.spdl = ""
|
||||
fp = open(filename,"r")
|
||||
for l in fp.readlines():
|
||||
self.spdl += l
|
||||
fp.close()
|
||||
|
||||
def verify(self):
|
||||
|
||||
if self.spdl:
|
||||
# Write spdl to temp file
|
||||
fp = tempfile.NamedTemporaryFile()
|
||||
fp.write(self.spdl)
|
||||
fp.flush()
|
||||
|
||||
# Run Scyther on temp file
|
||||
self.cmd = "%s %s --dot-output --xml-output --plain '%s'" % (self.program,self.options,fp.name)
|
||||
|
||||
# If we are on windows, we don't get stderr. Maybe we need a
|
||||
# switch to enforce this.
|
||||
if sys.platform.startswith('linux'):
|
||||
cmdline = "%s 2>/dev/null" % (self.cmd)
|
||||
else:
|
||||
# Non-linux does not generate stderr anyway
|
||||
cmdline = "%s" % (self.cmd)
|
||||
|
||||
result = os.popen(cmdline)
|
||||
|
||||
xmlinput = result.read()
|
||||
|
||||
|
||||
fp.close()
|
||||
|
||||
xmlfile = StringIO.StringIO(xmlinput)
|
||||
reader = XMLReader.XMLReader()
|
||||
self.claims = reader.readXML(xmlfile)
|
||||
|
||||
|
||||
# Cleanup
|
||||
del(xmlinput)
|
||||
del(xmlfile)
|
||||
return self.claims
|
||||
|
||||
else:
|
||||
# No input yet!
|
||||
return ""
|
||||
|
||||
def __str__(self):
|
||||
if self.claims:
|
||||
s = ""
|
||||
for cl in self.claims:
|
||||
s += str(cl) + "\n"
|
||||
return s
|
||||
else:
|
||||
return "Scyther has not been run yet."
|
||||
|
||||
|
||||
def basictest():
|
||||
# Some basic testing
|
||||
if sys.platform.startswith('win'):
|
||||
print "Dir test"
|
||||
p = os.popen("dir")
|
||||
print p.read()
|
||||
print p.close()
|
||||
confirm("See the dir?")
|
||||
|
||||
# Scyther
|
||||
x = Scyther()
|
||||
|
||||
if sys.platform.startswith('win'):
|
||||
x.program = "c:\\Scyther.exe"
|
||||
if not os.path.isfile(x.program):
|
||||
print "I can't find the Scyther executable %s" % (x.program)
|
||||
|
||||
x.setFile("ns3.spdl")
|
||||
x.verify()
|
||||
|
||||
if __name__ == '__main__':
|
||||
basictest()
|
||||
|
||||
|
97
gui/Scytherthread.py
Normal file
97
gui/Scytherthread.py
Normal file
@ -0,0 +1,97 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
""" Import externals """
|
||||
import wx
|
||||
import wx.lib.newevent
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import threading
|
||||
import StringIO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
""" Import scyther components """
|
||||
import XMLReader
|
||||
|
||||
""" Import scyther-gui components """
|
||||
import Tempfile
|
||||
import Claim
|
||||
import Preference
|
||||
import Scyther
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
""" Global declaration """
|
||||
(UpdateAttackEvent, EVT_UPDATE_ATTACK) = wx.lib.newevent.NewEvent()
|
||||
busy = threading.Semaphore()
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
def ScytherPath():
|
||||
""" Retrieve Scyther path, and maybe test whether is is valid? """
|
||||
program = Preference.get('scyther','scyther')
|
||||
program = "\\\\Roivas\\public\\scyther-gui\\Scyther.exe"
|
||||
return program
|
||||
|
||||
class ScytherThread(threading.Thread):
|
||||
# Override Thread's __init__ method to accept the parameters needed:
|
||||
def __init__ ( self, win, spdl, details ):
|
||||
|
||||
self.win = win
|
||||
self.spdl = spdl
|
||||
self.details = details
|
||||
|
||||
self.claims = []
|
||||
|
||||
threading.Thread.__init__ ( self )
|
||||
|
||||
def run(self):
|
||||
|
||||
global busy
|
||||
|
||||
evt = UpdateAttackEvent(status="Running Scyther...")
|
||||
wx.PostEvent(self.win, evt)
|
||||
|
||||
self.claimResults()
|
||||
|
||||
evt = UpdateAttackEvent(status="Done.")
|
||||
wx.PostEvent(self.win, evt)
|
||||
|
||||
#self.win.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
|
||||
busy.release()
|
||||
|
||||
def claimResults(self):
|
||||
""" Convert spdl to result (using Scyther) """
|
||||
|
||||
scyther = Scyther.Scyther()
|
||||
if sys.platform.startswith('win'):
|
||||
scyther.program = "c:\\Scyther.exe"
|
||||
if not os.path.isfile(scyther.program):
|
||||
print "I can't find the Scyther executable %s" % (scyther.program)
|
||||
|
||||
scyther.setInput(self.spdl)
|
||||
self.claims = scyther.verify()
|
||||
self.summary = str(scyther)
|
||||
|
||||
self.win.errors.update(self.summary)
|
||||
|
||||
|
||||
def RunScyther(win,mode):
|
||||
|
||||
global busy
|
||||
|
||||
if (busy.acquire(False)):
|
||||
|
||||
# start the thread
|
||||
win.SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
|
||||
|
||||
win.settings.mode = mode
|
||||
t = ScytherThread(win,win.control.GetValue(),"")
|
||||
t.start()
|
||||
win.threads = [ t ]
|
||||
|
||||
|
41
gui/Tempfile.py
Normal file
41
gui/Tempfile.py
Normal file
@ -0,0 +1,41 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
""" Import externals """
|
||||
import os
|
||||
import tempfile
|
||||
import atexit
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
""" Local thing (can be done in numerous nicer ways) """
|
||||
tempfiles = []
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
def tempremove(tuple):
|
||||
(fd,fpname) = tuple
|
||||
#os.close(fd)
|
||||
os.remove(fpname)
|
||||
|
||||
def cleanupshop():
|
||||
global tempfiles
|
||||
|
||||
for tuple in tempfiles:
|
||||
tempremove(tuple)
|
||||
|
||||
def tempcleaned(post=""):
|
||||
global tempfiles
|
||||
|
||||
tuple = tempfile.mkstemp(post,"scyther_")
|
||||
tempfiles.append(tuple)
|
||||
return tuple
|
||||
|
||||
def tempcleanearly(tuple):
|
||||
global tempfiles
|
||||
|
||||
tempfiles.remove(tuple)
|
||||
tempremove(tuple)
|
||||
|
||||
atexit.register(cleanupshop)
|
204
gui/Term.py
Normal file
204
gui/Term.py
Normal file
@ -0,0 +1,204 @@
|
||||
#
|
||||
# Term
|
||||
#
|
||||
import Trace
|
||||
from Misc import *
|
||||
|
||||
class InvalidTerm(TypeError):
|
||||
"Exception used to indicate that a given term is invalid"
|
||||
|
||||
|
||||
class Knowledge(object):
|
||||
def __init__(self,attack):
|
||||
self.attack = attack
|
||||
self.knowledge = []
|
||||
|
||||
def getInverse(self,term):
|
||||
for pair in self.attack.inverseKeys:
|
||||
if term == pair[0]:
|
||||
return pair[1]
|
||||
if term == pair[1]:
|
||||
return pair[0]
|
||||
|
||||
# Get the inverse key
|
||||
def getInverseKey(self,term):
|
||||
# First try to see if the entire term has an inverse
|
||||
result = self.getInverse(term)
|
||||
if result != None:
|
||||
return result
|
||||
|
||||
# If it is an apply term, try to see if the function has an inverse
|
||||
if isinstance(term,TermApply):
|
||||
result = self.getInverse(term.function)
|
||||
if result != None:
|
||||
return TermApply(result,term.argument)
|
||||
|
||||
# No inverse found, so term is its own inverse
|
||||
return term
|
||||
|
||||
# Add a term to the knowledge
|
||||
def add(self,term):
|
||||
if term == None:
|
||||
return
|
||||
added = False
|
||||
for x in term.deriveTerms(self):
|
||||
if not x in self.knowledge:
|
||||
added = True
|
||||
self.knowledge.append(x)
|
||||
|
||||
# Something new was added, maybe this can help us to decrypt a term
|
||||
# that we could not decrypt before
|
||||
if added:
|
||||
for x in self.knowledge:
|
||||
if isinstance(x,TermEncrypt):
|
||||
self.add(x)
|
||||
|
||||
def canDerive(self,term):
|
||||
# We can derive free variables, because we can even choose them
|
||||
if isinstance(term,TermVariable) and term.isFree():
|
||||
return True
|
||||
# We can derive a term if it is in the knowledge
|
||||
# or all terms required to construct it are in the knowledge
|
||||
if exists(lambda x: x == term,self.knowledge):
|
||||
return True
|
||||
constructors = term.constructorTerms()
|
||||
|
||||
if len(constructors) == 1 and constructors[0] == term:
|
||||
# This is a single term, there is no need to look at constructor
|
||||
# terms as we have already looked at the complete term
|
||||
return False
|
||||
|
||||
return forall(lambda x: self.canDerive(x),constructors)
|
||||
|
||||
|
||||
# Knowledge is the initial knowledge and all messages in sends
|
||||
def buildKnowledge(self):
|
||||
self.knowledge = self.attack.initialKnowledge[:]
|
||||
for run in self.attack.semiTrace.runs:
|
||||
# Intruder actions do not add knowledge processing them
|
||||
# is a waste of time
|
||||
if run.intruder:
|
||||
continue
|
||||
for event in run:
|
||||
if isinstance(event,Trace.EventSend):
|
||||
self.add(event.message)
|
||||
self.add(event.fr)
|
||||
self.add(event.to)
|
||||
|
||||
class Term(object):
|
||||
def __init__(self):
|
||||
self.types = None
|
||||
|
||||
def __str__(self):
|
||||
raise InvalidTerm
|
||||
|
||||
def constructorTerms(self):
|
||||
raise InvalidTerm
|
||||
|
||||
def deriveTerms(self,knowledge):
|
||||
raise InvalidTerm
|
||||
|
||||
# Two terms are equal when their string rep is equal
|
||||
def __cmp__(self,other):
|
||||
return cmp(str(self),str(other))
|
||||
|
||||
|
||||
class TermConstant(Term):
|
||||
def __init__(self, constant):
|
||||
Term.__init__(self)
|
||||
self.value = str(constant)
|
||||
|
||||
def deriveTerms(self,knowledge):
|
||||
return [self]
|
||||
|
||||
def constructorTerms(self):
|
||||
return [self]
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
class TermEncrypt(Term):
|
||||
def __init__(self, value, key):
|
||||
Term.__init__(self)
|
||||
self.value = value
|
||||
self.key = key
|
||||
|
||||
def deriveTerms(self,knowledge):
|
||||
# In order to unpack an encrypted term we have to have the inverse key
|
||||
inverse = knowledge.getInverseKey(self.key)
|
||||
if knowledge.canDerive(inverse):
|
||||
return [self] + [self.value] + self.value.deriveTerms(knowledge)
|
||||
else:
|
||||
return [self]
|
||||
|
||||
def constructorTerms(self):
|
||||
return [self.value,self.key]
|
||||
|
||||
def __str__(self):
|
||||
return "{%s}%s" % (self.value, self.key)
|
||||
|
||||
class TermApply(Term):
|
||||
def __init__(self, function, argument):
|
||||
Term.__init__(self)
|
||||
self.function = function
|
||||
self.argument = argument
|
||||
|
||||
def constructorTerms(self):
|
||||
return [self.function,self.argument]
|
||||
|
||||
def deriveTerms(self,knowledge):
|
||||
return [self]
|
||||
|
||||
def __str__(self):
|
||||
return "%s(%s)" % (self.function, self.argument)
|
||||
|
||||
class TermVariable(Term):
|
||||
def __init__(self, name, value):
|
||||
Term.__init__(self)
|
||||
self.name = name
|
||||
self.value = value
|
||||
|
||||
def isFree(self):
|
||||
return self.value == None
|
||||
|
||||
def constructorTerms(self):
|
||||
if self.value != None:
|
||||
return [self.value]
|
||||
else:
|
||||
return [self.name]
|
||||
|
||||
def deriveTerms(self,knowledge):
|
||||
if self.value != None:
|
||||
return [self,self.value] + self.value.deriveTerms(knowledge)
|
||||
else:
|
||||
return [self,self.name]
|
||||
|
||||
def __str__(self):
|
||||
if (self.value != None):
|
||||
return str(self.value)
|
||||
else:
|
||||
return str(self.name)
|
||||
|
||||
class TermTuple(Term):
|
||||
def __init__(self, op1, op2):
|
||||
Term.__init__(self)
|
||||
self.op1 = op1
|
||||
self.op2 = op2
|
||||
|
||||
def __str__(self):
|
||||
return "%s,%s" % (self.op1,self.op2)
|
||||
|
||||
def constructorTerms(self):
|
||||
return [self.op1,self.op2]
|
||||
|
||||
def deriveTerms(self,knowledge):
|
||||
return [self,self.op1,self.op2]+self.op1.deriveTerms(knowledge)+self.op2.deriveTerms(knowledge)
|
||||
|
||||
def __getitem__(self,index):
|
||||
if index == 0:
|
||||
return self.op1
|
||||
elif index == 1:
|
||||
return self.op2
|
||||
else:
|
||||
return self.op2.__getitem__(index-1)
|
||||
|
309
gui/Trace.py
Normal file
309
gui/Trace.py
Normal file
@ -0,0 +1,309 @@
|
||||
#
|
||||
# Trace
|
||||
#
|
||||
from Misc import *
|
||||
|
||||
class InvalidAction(TypeError):
|
||||
"Exception used to indicate that a given action is invalid"
|
||||
|
||||
class InvalidEvent(TypeError):
|
||||
"Exception used to indicate that a given event is invalid"
|
||||
|
||||
class SemiTrace(object):
|
||||
def __init__(self):
|
||||
self.runs = []
|
||||
|
||||
def totalCount(self):
|
||||
count = 0
|
||||
for run in self.runs:
|
||||
count += len(run.eventList)
|
||||
return count
|
||||
|
||||
def sortActions(self,actionlist):
|
||||
newlist = actionlist[:]
|
||||
newlist.sort(lambda x,y: self.getOrder(x,y))
|
||||
return newlist
|
||||
|
||||
def getEnabled(self,previous):
|
||||
enabled = []
|
||||
for run in self.runs:
|
||||
for event in run:
|
||||
if event in previous or event in enabled:
|
||||
continue
|
||||
prec = self.getPrecedingEvents(event,previous)
|
||||
if len(prec) == 0:
|
||||
enabled.append(event)
|
||||
return enabled
|
||||
|
||||
# Returns run,index tuples for all connections
|
||||
def getConnections(self,event,removeIntruder=False):
|
||||
if not removeIntruder:
|
||||
return event.follows
|
||||
result = []
|
||||
if event.run.intruder:
|
||||
for before in event.getBefore():
|
||||
result.extend(self.getConnections(before,removeIntruder))
|
||||
|
||||
for x in event.follows:
|
||||
fol = self.getEvent(x)
|
||||
# If this is an intruder action descend into it
|
||||
if fol.run.intruder:
|
||||
result.extend(self.getConnections(fol,removeIntruder))
|
||||
else:
|
||||
result.append(x)
|
||||
return uniq(result)
|
||||
|
||||
# Return the minimum set of preceding events for a given event
|
||||
# that is the events before this event in the same run and all
|
||||
# actions required by the partional ordering
|
||||
# If previous is non empty remove all events already in previous
|
||||
def getPrecedingEvents(self,event,previous=[]):
|
||||
# If it is cached return cached version
|
||||
if event.preceding != None:
|
||||
return filter(lambda x: x not in previous,event.preceding)
|
||||
preceding = []
|
||||
for prec in event.getBefore():
|
||||
preceding.append(prec)
|
||||
preceding.extend(self.getPrecedingEvents(prec))
|
||||
for x in event.follows:
|
||||
fol = self.getEvent(x)
|
||||
preceding.append(fol)
|
||||
preceding.extend(self.getPrecedingEvents(fol))
|
||||
preceding = uniq(preceding)
|
||||
event.preceding = preceding
|
||||
preceding = filter(lambda x: x not in previous,preceding)
|
||||
return preceding
|
||||
|
||||
# Returns -1 if the first event has to be before the second one
|
||||
# +1 if the second event has to be before the first one
|
||||
# 0 if there is no order defined on the two events
|
||||
def getOrder(self,event1,event2):
|
||||
if (event1 in self.getPrecedingEvents(event2)):
|
||||
return -1
|
||||
if (event2 in self.getPrecedingEvents(event1)):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
# Get event by run id and index
|
||||
def getEvent(self,idx):
|
||||
(rid,index) = idx
|
||||
for run in self.runs:
|
||||
if run.id != rid:
|
||||
continue
|
||||
for event in run:
|
||||
if event.index == index:
|
||||
return event
|
||||
raise InvalidEvent
|
||||
|
||||
# Get all claim events in the trace
|
||||
def getClaims(self):
|
||||
claims = []
|
||||
for run in self.runs:
|
||||
for event in run:
|
||||
if isinstance(event,EventClaim):
|
||||
claims.append(event)
|
||||
return claims
|
||||
|
||||
# Returns a list of all initiation events in the semitrace
|
||||
def getInitiations(self):
|
||||
initiations = []
|
||||
for run in self.runs:
|
||||
# Initiations are runs of honest agents
|
||||
if (run.intruder):
|
||||
continue
|
||||
# Which contain no reads before the first send
|
||||
for action in run:
|
||||
if (isinstance(action,EventRead)):
|
||||
break
|
||||
elif (isinstance(action,EventSend)):
|
||||
initiations.append(action)
|
||||
break
|
||||
return initiations
|
||||
|
||||
# Get all runs performed by a specific agent
|
||||
def getAgentRuns(self,agent):
|
||||
result = []
|
||||
for run in self.runs:
|
||||
if run.getAgent() == agent:
|
||||
result.append(run)
|
||||
return result
|
||||
|
||||
# Return a list of all runs that are parallel with this run
|
||||
def getParallelRuns(self,run):
|
||||
parallel = []
|
||||
first = run.getFirstAction()
|
||||
# Process all events that are before the end of the run
|
||||
for event in self.getPrecedingEvents(run.getLastAction()):
|
||||
# Only count those we haven't found yet
|
||||
if event.run in parallel or event.run == run:
|
||||
continue
|
||||
# If the event is also after the beginning of the run it is
|
||||
# parallel
|
||||
if self.getOrder(event,first) == 1:
|
||||
parallel.append(event.run)
|
||||
return parallel
|
||||
|
||||
def getRun(self,runid):
|
||||
for run in self.runs:
|
||||
if run.id == runid:
|
||||
return run
|
||||
return None
|
||||
|
||||
class ProtocolDescription(object):
|
||||
def __init__(self,protocol):
|
||||
self.protocol = protocol
|
||||
self.roledescr = {}
|
||||
|
||||
# Find event by label
|
||||
def findEvent(self,eventlabel,eventType=None):
|
||||
for (role,descr) in self.roledescr.items():
|
||||
for event in descr:
|
||||
if event.label == eventlabel:
|
||||
if eventType == None or isinstance(event,eventType):
|
||||
return event
|
||||
|
||||
# Return all events that should have occured before the given event
|
||||
# if the protocol is executed exactly as specified
|
||||
# (i.e. all previous events in the same run and the preceding events
|
||||
# of the matching sends of all reads)
|
||||
def getPrecedingEvents(self,eventlabel,eventType=None):
|
||||
event = self.findEvent(eventlabel,eventType)
|
||||
if event.preceding != None:
|
||||
return event.preceding
|
||||
preceding = event.getBefore()+[event]
|
||||
for prev in preceding:
|
||||
# For this event and all events that are before it in the run
|
||||
# description see if it is a read and if it is also add the
|
||||
# precedinglabelset of the matching send
|
||||
if (isinstance(prev,EventRead)):
|
||||
match = self.findEvent(prev.label,EventSend)
|
||||
if match:
|
||||
preceding.extend(self.getPrecedingEvents(match.label,EventSend))
|
||||
preceding = uniq(preceding)
|
||||
event.preceding = preceding
|
||||
return preceding
|
||||
|
||||
# Calculate the preceding labelset that is all read events
|
||||
# that are in the precedingEvents of a certain event
|
||||
def getPrecedingLabelSet(self,eventlabel):
|
||||
events = self.getPrecedingEvents(eventlabel)
|
||||
events = filter(lambda x: isinstance(x,EventRead),events)
|
||||
return [x.label for x in events]
|
||||
|
||||
# Calculate the roles in preceding labelset that is all roles that
|
||||
# that are in the precedingEvents of a certain event
|
||||
def getPrecedingRoleSet(self,eventlabel):
|
||||
events = self.getPrecedingEvents(eventlabel)
|
||||
roles = uniq([x.run.role for x in events])
|
||||
return roles
|
||||
|
||||
|
||||
def __str__(self):
|
||||
s = ''
|
||||
for x in self.roledescr.values():
|
||||
for e in x:
|
||||
s += str(e) + "\n"
|
||||
return s
|
||||
|
||||
class Run(object):
|
||||
def __init__(self):
|
||||
self.id = None
|
||||
self.protocol = None
|
||||
self.role = None
|
||||
self.roleAgents = {}
|
||||
self.eventList = []
|
||||
self.intruder = False
|
||||
self.attack = None
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.eventList)
|
||||
|
||||
def getAgent(self):
|
||||
if self.intruder:
|
||||
return None
|
||||
return self.roleAgents[self.role]
|
||||
|
||||
def getFirstAction(self):
|
||||
return self.eventList[0]
|
||||
|
||||
def getLastAction(self):
|
||||
return self.eventList[-1]
|
||||
|
||||
class Event(object):
|
||||
def __init__(self,index,label,follows):
|
||||
self.index = index
|
||||
self.label = label
|
||||
self.follows = follows
|
||||
self.run = None
|
||||
self.preceding = None
|
||||
self.rank = None
|
||||
|
||||
def shortLabel(self):
|
||||
try:
|
||||
return self.label[len(self.label)-1]
|
||||
except:
|
||||
return str(self.label)
|
||||
|
||||
def getBefore(self):
|
||||
result = []
|
||||
for event in self.run:
|
||||
if (event == self):
|
||||
return result
|
||||
result.append(event)
|
||||
# This should never happen
|
||||
assert(False)
|
||||
|
||||
class EventSend(Event):
|
||||
def __init__(self,index,label,follows,fr,to,message):
|
||||
Event.__init__(self,index,label,follows)
|
||||
self.fr = fr
|
||||
self.to = to
|
||||
self.message = message
|
||||
|
||||
def __str__(self):
|
||||
if self.run.intruder:
|
||||
return "SEND(%s)" % self.message
|
||||
else:
|
||||
return "SEND_%s(%s,%s)" % (self.shortLabel(),self.to,self.message)
|
||||
|
||||
class EventRead(Event):
|
||||
def __init__(self,index,label,follows,fr,to,message):
|
||||
Event.__init__(self,index,label,follows)
|
||||
self.fr = fr
|
||||
self.to = to
|
||||
self.message = message
|
||||
|
||||
def __str__(self):
|
||||
if self.run.intruder:
|
||||
return "READ(%s)" % self.message
|
||||
else:
|
||||
return "READ_%s(%s,%s)" % (self.shortLabel(),self.fr, self.message)
|
||||
|
||||
class EventClaim(Event):
|
||||
def __init__(self,index,label,follows,role,type,argument):
|
||||
Event.__init__(self,index,label,follows)
|
||||
self.role = role
|
||||
self.type = type
|
||||
self.argument = argument
|
||||
self.broken = None
|
||||
|
||||
# A Claim should be ignored if there is an untrusted agent in the role
|
||||
# agents
|
||||
def ignore(self):
|
||||
for untrusted in self.run.attack.untrusted:
|
||||
if untrusted in self.run.roleAgents.values():
|
||||
return True
|
||||
return False
|
||||
|
||||
# Return (protocol,role)
|
||||
def protocolRole(self):
|
||||
return "(%s,%s)" % (self.run.protocol,self.run.role)
|
||||
|
||||
def argstr(self):
|
||||
if self.argument == None:
|
||||
return '*'
|
||||
else:
|
||||
return str(self.argument)
|
||||
|
||||
def __str__(self):
|
||||
return "CLAIM_%s(%s, %s)" % (self.shortLabel(),self.type,self.argstr())
|
301
gui/XMLReader.py
Normal file
301
gui/XMLReader.py
Normal file
@ -0,0 +1,301 @@
|
||||
#
|
||||
# XMLReader
|
||||
#
|
||||
# Note:
|
||||
# This requires python elementtree to work
|
||||
# See: http://effbot.org/zone/element-index.htm
|
||||
#
|
||||
# On Fedora Core you can install this by installing the python-elementtree rpm
|
||||
# Things will be a lot faster and consume less memory if you install the
|
||||
# cElementTree module
|
||||
#
|
||||
|
||||
# Check for cElementTree presence. Otherwise use ElementTree.
|
||||
useiter = True
|
||||
try:
|
||||
import cElementTree
|
||||
except ImportError:
|
||||
useiter = False
|
||||
from elementtree import ElementTree
|
||||
|
||||
## Simply pick cElementTree
|
||||
#import cElementTree
|
||||
## Simply pick ElementTree
|
||||
#useiter = False
|
||||
#from elementtree import ElementTree
|
||||
|
||||
import Term
|
||||
import Attack
|
||||
import Trace
|
||||
import Claim
|
||||
import sys
|
||||
|
||||
class XMLReader(object):
|
||||
|
||||
def __init__ (self):
|
||||
self.varlist = []
|
||||
pass
|
||||
|
||||
def readXML(self, input):
|
||||
# Use iter parse when possble so we can clear the attack after reading
|
||||
# it in order to preserve memory (this requires cElementTree)
|
||||
|
||||
attackbuffer = []
|
||||
claims = []
|
||||
|
||||
if useiter:
|
||||
parser = cElementTree.iterparse(input)
|
||||
else:
|
||||
parser = ElementTree.parse(input).findall('*')
|
||||
|
||||
for elem in parser:
|
||||
# The iter parser receives the input in tuples (event and element)
|
||||
# we only need the event
|
||||
if useiter:
|
||||
elem = elem[1]
|
||||
|
||||
if elem.tag == 'state':
|
||||
attack = self.readAttack(elem)
|
||||
attackbuffer.append(attack)
|
||||
if useiter:
|
||||
elem.clear()
|
||||
|
||||
if elem.tag == 'claimstatus':
|
||||
claim = self.readClaim(elem)
|
||||
claim.attacks = attackbuffer
|
||||
claims.append(claim)
|
||||
|
||||
# link to parent
|
||||
for attack in claim.attacks:
|
||||
attack.claim = claim
|
||||
|
||||
attackbuffer = []
|
||||
if useiter:
|
||||
elem.clear()
|
||||
|
||||
return claims
|
||||
|
||||
# Read a term from XML
|
||||
def readTerm(self,xml):
|
||||
# If xml is None the term should also be none
|
||||
if xml == None:
|
||||
return None
|
||||
# If this is a term variable read it directly
|
||||
if (xml.tag in ('tuple','const','apply','encrypt','var')):
|
||||
return self.readSubTerm(xml)
|
||||
# Otherwise read from it's first child
|
||||
children = xml.getchildren()
|
||||
assert(len(children) == 1)
|
||||
return self.readSubTerm(children[0])
|
||||
|
||||
def readSubTerm(self, tag):
|
||||
if tag.tag == 'tuple':
|
||||
return Term.TermTuple(self.readTerm(tag.find('op1')),self.readTerm(tag.find('op2')))
|
||||
elif tag.tag == 'const':
|
||||
return Term.TermConstant(tag.text)
|
||||
elif tag.tag == 'apply':
|
||||
return Term.TermApply(self.readTerm(tag.find('function')),self.readTerm(tag.find('arg')))
|
||||
elif tag.tag == 'encrypt':
|
||||
return Term.TermEncrypt(self.readTerm(tag.find('op')),self.readTerm(tag.find('key')))
|
||||
elif tag.tag == 'var':
|
||||
name = Term.TermConstant(tag.get('name'))
|
||||
# Instantiate this variable if possible (note this list is empty while reading
|
||||
# the variables section of the XML file)
|
||||
for inst in self.varlist:
|
||||
if inst.name == name:
|
||||
return inst
|
||||
# If it is not instantiated in varlist, just return a variable with this name and no
|
||||
# value
|
||||
return Term.TermVariable(name,None)
|
||||
else:
|
||||
raise Term.InvalidTerm, "Invalid term type in XML: %s" % tag.tag
|
||||
|
||||
def readEvent(self,xml):
|
||||
label = self.readTerm(xml.find('label'))
|
||||
follows = xml.findall('follows')
|
||||
followlist = []
|
||||
for follow in follows:
|
||||
follow = follow.find('after')
|
||||
if follow == None:
|
||||
# Ignore follow definitions that do not contain after
|
||||
continue
|
||||
follow = (int(follow.get('run')),int(follow.get('index')))
|
||||
followlist.append(follow)
|
||||
|
||||
(etype,index) = (xml.get('type'),int(xml.get('index')))
|
||||
if etype in ('send','read'):
|
||||
fr = self.readTerm(xml.find('from'))
|
||||
to = self.readTerm(xml.find('to'))
|
||||
message = self.readTerm(xml.find('message'))
|
||||
if (etype == 'send'):
|
||||
return Trace.EventSend(index,label,followlist,fr,to,message)
|
||||
else:
|
||||
return Trace.EventRead(index,label,followlist,fr,to,message)
|
||||
elif xml.get('type') == 'claim':
|
||||
role = self.readTerm(xml.find('role'))
|
||||
etype = self.readTerm(xml.find('type'))
|
||||
argument = self.readTerm(xml.find('argument'))
|
||||
# Freshness claims are implemented as Empty claims with
|
||||
# (Fresh,Value) as arguments
|
||||
try:
|
||||
if etype == 'Empty' and argument[0] == 'Fresh':
|
||||
etype = Term.TermConstant('Fresh')
|
||||
argument = argument[1]
|
||||
elif etype == 'Empty' and argument[0] == 'Compromised':
|
||||
etype = Term.TermConstant('Compromised')
|
||||
argument = argument[1]
|
||||
except:
|
||||
pass
|
||||
return Trace.EventClaim(index,label,followlist,role,etype,argument)
|
||||
else:
|
||||
raise Trace.InvalidAction, "Invalid action in XML: %s" % (xml.get('type'))
|
||||
|
||||
def readRun(self,xml):
|
||||
assert(xml.tag == 'run')
|
||||
run = Trace.Run()
|
||||
run.id = int(xml.find('runid').text)
|
||||
# TODO why is protocol name a term??
|
||||
run.protocol = str(self.readTerm(xml.find('protocol')))
|
||||
run.intruder = xml.find('protocol').get('intruder') == 'true'
|
||||
run.role = xml.find('rolename').text
|
||||
for role in xml.find('roleagents'):
|
||||
name = role.find('rolename').text
|
||||
agent = self.readTerm(role.find('agent'))
|
||||
run.roleAgents[name] = agent
|
||||
for eventxml in xml.find('eventlist'):
|
||||
action = self.readEvent(eventxml)
|
||||
action.run = run
|
||||
run.eventList.append(action)
|
||||
return run
|
||||
|
||||
# Read protocol description for a certain role
|
||||
def readRoleDescr(self,xml):
|
||||
assert(xml.tag == 'role')
|
||||
run = Trace.Run()
|
||||
# We will need the last label later on to see if a
|
||||
# run is complete
|
||||
run.lastLabel = None
|
||||
run.role = xml.find('rolename').text
|
||||
for eventxml in xml.find('eventlist'):
|
||||
action = self.readEvent(eventxml)
|
||||
action.run = run
|
||||
run.eventList.append(action)
|
||||
run.lastLabel = action.label
|
||||
return run
|
||||
|
||||
def readTypeList(self,xml):
|
||||
result = []
|
||||
vartypes = xml.find('type').find('termlist')
|
||||
for vartype in vartypes:
|
||||
# We will assume that types are simple strings
|
||||
result.append(str(self.readTerm(vartype)))
|
||||
return result
|
||||
|
||||
def readClaim(self, xml):
|
||||
claim = Claim.Claim()
|
||||
for event in xml.getchildren():
|
||||
if event.tag == 'claimtype':
|
||||
claim.claimtype = self.readTerm(event)
|
||||
elif event.tag == 'label':
|
||||
# We store the full protocol,label construct for
|
||||
# consistency with the technical parts, so it is left to
|
||||
# the __str__ of claim to select the right element
|
||||
claim.label = self.readTerm(event)
|
||||
elif event.tag == 'protocol':
|
||||
claim.protocol = self.readTerm(event)
|
||||
elif event.tag == 'role':
|
||||
claim.role = self.readTerm(event)
|
||||
elif event.tag == 'parameter':
|
||||
claim.parameter = self.readTerm(event)
|
||||
|
||||
elif event.tag == 'failed':
|
||||
claim.failed = int(event.text)
|
||||
elif event.tag == 'count':
|
||||
claim.count = int(event.text)
|
||||
elif event.tag == 'states':
|
||||
claim.states = int(event.text)
|
||||
|
||||
elif event.tag == 'complete':
|
||||
claim.complete = True
|
||||
elif event.tag == 'timebound':
|
||||
claim.timebound = True
|
||||
else:
|
||||
print >>sys.stderr,"Warning unknown tag in claim: %s" % claim.tag
|
||||
|
||||
claim.analyze()
|
||||
return claim
|
||||
|
||||
def readAttack(self, xml):
|
||||
self.varlist = []
|
||||
attack = Attack.Attack()
|
||||
attack.id = int(xml.get('id'))
|
||||
# A state contains 4 direct child nodes:
|
||||
# broken, system, variables and semitrace
|
||||
# optionally a fifth: dot
|
||||
for event in xml.getchildren():
|
||||
if event.tag == 'broken':
|
||||
attack.broken.append((self.readTerm(event.find('claim')),
|
||||
self.readTerm(event.find('label'))))
|
||||
elif event.tag == 'system':
|
||||
attack.match = int(event.find('match').text)
|
||||
for term in event.find('commandline'):
|
||||
if attack.commandline != '':
|
||||
attack.commandline += ' '
|
||||
attack.commandline += term.text
|
||||
for term in event.find('untrusted').find('termlist'):
|
||||
attack.untrusted.append(str(self.readTerm(term)))
|
||||
for term in event.find('initialknowledge').find('termlist'):
|
||||
attack.initialKnowledge.append(self.readTerm(term))
|
||||
for keypair in event.find('inversekeys'):
|
||||
inverse = []
|
||||
for term in keypair:
|
||||
inverse.append(self.readTerm(term))
|
||||
assert(len(inverse) == 0 or len(inverse) == 2)
|
||||
attack.inverseKeys.append(inverse)
|
||||
# TODO why is protocol name a term??
|
||||
for protocolxml in event.findall('protocol'):
|
||||
protocol = str(self.readTerm(protocolxml.find('name')))
|
||||
descr = Trace.ProtocolDescription(protocol)
|
||||
attack.protocoldescr[protocol] = descr
|
||||
for rolexml in protocolxml.findall('role'):
|
||||
roledescr = self.readRoleDescr(rolexml)
|
||||
descr.roledescr[roledescr.role] = roledescr
|
||||
|
||||
elif event.tag == 'semitrace':
|
||||
for runxml in event:
|
||||
run = self.readRun(runxml)
|
||||
run.attack = attack
|
||||
attack.semiTrace.runs.append(run)
|
||||
|
||||
elif event.tag == 'dot':
|
||||
# Apparently Scyther already generated dot output,
|
||||
# store
|
||||
attack.scytherDot = event.text
|
||||
|
||||
elif event.tag == 'variables':
|
||||
# Read the variables one by one
|
||||
for varxml in event:
|
||||
if varxml.get('typeflaw') == 'true':
|
||||
attack.typeflaws = True
|
||||
var = self.readTerm(varxml.find('name').find('term'))
|
||||
var.types = self.readTypeList(varxml.find('name'))
|
||||
|
||||
substxml = varxml.find('substitution')
|
||||
# Read substitution if present
|
||||
if substxml != None:
|
||||
subst = self.readTerm(substxml.find('term'))
|
||||
subst.types = self.readTypeList(substxml)
|
||||
newvar = Term.TermVariable(var.name,subst)
|
||||
newvar.types = var.types
|
||||
var = newvar
|
||||
|
||||
attack.variables.append(var)
|
||||
|
||||
# When all have been read set self.varlist so that when
|
||||
# we read terms in the attacks they can be filled in using
|
||||
# this list
|
||||
self.varlist = attack.variables
|
||||
else:
|
||||
print >>sys.stderr,"Warning unknown tag in attack: %s" % event.tag
|
||||
return attack
|
||||
|
222
gui/images/scyther-icon.svg
Normal file
222
gui/images/scyther-icon.svg
Normal file
@ -0,0 +1,222 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://web.resource.org/cc/"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="841.88977pt"
|
||||
height="595.27557pt"
|
||||
id="svg2"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.43"
|
||||
version="1.0"
|
||||
sodipodi:docbase="/home/cas/bzr/scyther-gui/images"
|
||||
sodipodi:docname="scyther-icon.svg"
|
||||
inkscape:export-filename="/home/cas/svn/scyther/Design download page Scyther/scyther-icon16.png"
|
||||
inkscape:export-xdpi="1.6494846"
|
||||
inkscape:export-ydpi="1.6494846">
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="linearGradient11078">
|
||||
<stop
|
||||
id="stop11080"
|
||||
offset="0"
|
||||
style="stop-color:#000000;stop-opacity:0.57731956;" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0;"
|
||||
offset="0.209216"
|
||||
id="stop11084" />
|
||||
<stop
|
||||
id="stop11086"
|
||||
offset="0.65205503"
|
||||
style="stop-color:#000000;stop-opacity:0.2371134;" />
|
||||
<stop
|
||||
id="stop11082"
|
||||
offset="1"
|
||||
style="stop-color:#000000;stop-opacity:0.67010307;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient11052">
|
||||
<stop
|
||||
id="stop11054"
|
||||
offset="0"
|
||||
style="stop-color:#000000;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#276816;stop-opacity:1;"
|
||||
offset="0.25145975"
|
||||
id="stop11060" />
|
||||
<stop
|
||||
id="stop11066"
|
||||
offset="0.57149941"
|
||||
style="stop-color:#000000;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop11062"
|
||||
offset="0.57149941"
|
||||
style="stop-color:#010400;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#002583;stop-opacity:1;"
|
||||
offset="0.8028897"
|
||||
id="stop11064" />
|
||||
<stop
|
||||
id="stop11056"
|
||||
offset="1"
|
||||
style="stop-color:#000000;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient11044">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop11046" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop11048" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient7532">
|
||||
<stop
|
||||
id="stop7534"
|
||||
offset="0"
|
||||
style="stop-color:#9e9e9e;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop7536"
|
||||
offset="1"
|
||||
style="stop-color:#9e9e9e;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient11052"
|
||||
id="linearGradient5737"
|
||||
x1="521.7384"
|
||||
y1="589.8822"
|
||||
x2="521.7384"
|
||||
y2="174.88217"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient11044"
|
||||
id="linearGradient8364"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="323.7384"
|
||||
y1="278.88214"
|
||||
x2="477.7384"
|
||||
y2="322.88214" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="526.18109"
|
||||
inkscape:cy="372.04724"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:window-width="1280"
|
||||
inkscape:window-height="953"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0">
|
||||
<sodipodi:guide
|
||||
orientation="horizontal"
|
||||
position="296.88141"
|
||||
id="guide1366" />
|
||||
<sodipodi:guide
|
||||
orientation="horizontal"
|
||||
position="571.88122"
|
||||
id="guide8421" />
|
||||
<sodipodi:guide
|
||||
orientation="vertical"
|
||||
position="206.87725"
|
||||
id="guide8423" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Black base"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
style="display:inline"
|
||||
sodipodi:insensitive="true">
|
||||
<rect
|
||||
style="opacity:1;fill:url(#linearGradient5737);fill-opacity:1;stroke:#000000;stroke-width:6.73799992;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
id="rect7546"
|
||||
width="323.44028"
|
||||
height="320.51846"
|
||||
x="194.92915"
|
||||
y="162.62292" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer9"
|
||||
inkscape:label="inner fade"
|
||||
sodipodi:insensitive="true"
|
||||
style="display:inline">
|
||||
<rect
|
||||
style="opacity:1;fill:url(#linearGradient8364);fill-opacity:1;stroke:#000000;stroke-width:6.73799992;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
|
||||
id="rect7489"
|
||||
width="323.44028"
|
||||
height="320.51846"
|
||||
x="194.92915"
|
||||
y="162.62292" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer7"
|
||||
inkscape:label="blacker fades"
|
||||
style="display:inline"
|
||||
sodipodi:insensitive="true" />
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer4"
|
||||
inkscape:label="white inner"
|
||||
style="display:inline">
|
||||
<path
|
||||
style="font-size:48px;font-style:oblique;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:15.39999962;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:8.60000038;stroke-dasharray:none;stroke-opacity:1;display:inline;font-family:Luxi Serif"
|
||||
d="M 439.625,186.5625 L 228.875,186.96875 L 229,447.625 L 298.46875,447.0625 C 330.87282,446.92031 358.52897,437.28122 380.09375,420.40625 C 401.65809,403.53126 412.43728,382.80494 412.4375,358.21875 C 412.43728,345.47874 408.75517,334.539 401.375,325.375 C 394.57101,316.65817 381.16868,305.92772 361.21875,293.1875 L 346,283.46875 C 335.8349,277.00229 328.44922,270.61422 323.78125,264.34375 C 307.04223,258.53812 294.42062,259.0563 268.875,259.90625 C 278.05518,239.8488 296.11407,242.38722 300.21875,237.3125 C 304.95019,231.46292 305.43096,234.13065 309.84375,229.25 C 321.81701,216.0073 334.34348,209.33336 346.96875,206.0625 C 353.47544,203.07236 360.58299,201.56871 368.3125,201.65625 C 397.62337,201.98826 427.75001,225.34375 427.75,225.34375 L 439.625,186.5625 z "
|
||||
id="path7544"
|
||||
sodipodi:nodetypes="ccccsscccccsscscc" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer8"
|
||||
inkscape:label="upper white"
|
||||
style="display:inline">
|
||||
<path
|
||||
style="font-size:48px;font-style:oblique;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:8.89999962;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:8.60000038;stroke-dasharray:none;stroke-opacity:1;display:inline;font-family:Luxi Serif"
|
||||
d="M 439.625,186.5625 L 228.875,186.96875 L 229,447.625 L 298.46875,447.0625 C 330.87282,446.92031 358.52897,437.28122 380.09375,420.40625 C 401.65809,403.53126 412.43728,382.80494 412.4375,358.21875 C 412.43728,345.47874 408.75517,334.539 401.375,325.375 C 394.57101,316.65817 381.16868,305.92772 361.21875,293.1875 L 346,283.46875 C 335.8349,277.00229 328.44922,270.61422 323.78125,264.34375 C 307.04223,258.53812 294.42062,259.0563 268.875,259.90625 C 278.05518,239.8488 296.11407,242.38722 300.21875,237.3125 C 304.95019,231.46292 305.43096,234.13065 309.84375,229.25 C 321.81701,216.0073 334.34348,209.33336 346.96875,206.0625 C 353.47544,203.07236 360.58299,201.56871 368.3125,201.65625 C 397.62337,201.98826 427.75001,225.34375 427.75,225.34375 L 439.625,186.5625 z "
|
||||
id="path6613"
|
||||
sodipodi:nodetypes="ccccsscccccsscscc" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer5"
|
||||
inkscape:label="beak"
|
||||
style="display:inline"
|
||||
sodipodi:insensitive="true" />
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer3"
|
||||
inkscape:label="cyther"
|
||||
style="display:inline"
|
||||
sodipodi:insensitive="true" />
|
||||
</svg>
|
After Width: | Height: | Size: 8.5 KiB |
249
gui/images/scyther-logo.svg
Normal file
249
gui/images/scyther-logo.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 15 KiB |
BIN
gui/images/scyther-splash.png
Normal file
BIN
gui/images/scyther-splash.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 139 KiB |
BIN
gui/images/scyther-splash.xcf
Normal file
BIN
gui/images/scyther-splash.xcf
Normal file
Binary file not shown.
51
gui/ns3.spdl
Normal file
51
gui/ns3.spdl
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Needham-Schroeder protocol
|
||||
*/
|
||||
|
||||
// PKI infrastructure
|
||||
|
||||
const pk: Function;
|
||||
secret sk: Function;
|
||||
inversekeys (pk,sk);
|
||||
|
||||
// The protocol description
|
||||
|
||||
protocol ns3(I,R)
|
||||
{
|
||||
role I
|
||||
{
|
||||
const ni: Nonce;
|
||||
var nr: Nonce;
|
||||
|
||||
send_1(I,R, {I,ni}pk(R) );
|
||||
read_2(R,I, {ni,nr}pk(I) );
|
||||
send_3(I,R, {nr}pk(R) );
|
||||
|
||||
claim_i1(I,Secret,ni);
|
||||
claim_i2(I,Secret,nr);
|
||||
claim_i3(I,Niagree);
|
||||
claim_i4(I,Nisynch);
|
||||
}
|
||||
|
||||
role R
|
||||
{
|
||||
var ni: Nonce;
|
||||
const nr: Nonce;
|
||||
|
||||
read_1(I,R, {I,ni}pk(R) );
|
||||
send_2(R,I, {ni,nr}pk(I) );
|
||||
read_3(I,R, {nr}pk(R) );
|
||||
|
||||
claim_r1(R,Secret,ni);
|
||||
claim_r2(R,Secret,nr);
|
||||
claim_r3(R,Niagree);
|
||||
claim_r4(R,Nisynch);
|
||||
}
|
||||
}
|
||||
|
||||
// An untrusted agent, with leaked information
|
||||
|
||||
const Eve: Agent;
|
||||
untrusted Eve;
|
||||
compromised sk(Eve);
|
||||
|
51
gui/nsl3.spdl
Normal file
51
gui/nsl3.spdl
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Needham-Schroeder-Lowe protocol
|
||||
*/
|
||||
|
||||
// PKI infrastructure
|
||||
|
||||
const pk: Function;
|
||||
secret sk: Function;
|
||||
inversekeys (pk,sk);
|
||||
|
||||
// The protocol description
|
||||
|
||||
protocol nsl3(I,R)
|
||||
{
|
||||
role I
|
||||
{
|
||||
const ni: Nonce;
|
||||
var nr: Nonce;
|
||||
|
||||
send_1(I,R, {I,ni}pk(R) );
|
||||
read_2(R,I, {ni,nr,R}pk(I) );
|
||||
send_3(I,R, {nr}pk(R) );
|
||||
|
||||
claim_i1(I,Secret,ni);
|
||||
claim_i2(I,Secret,nr);
|
||||
claim_i3(I,Niagree);
|
||||
claim_i4(I,Nisynch);
|
||||
}
|
||||
|
||||
role R
|
||||
{
|
||||
var ni: Nonce;
|
||||
const nr: Nonce;
|
||||
|
||||
read_1(I,R, {I,ni}pk(R) );
|
||||
send_2(R,I, {ni,nr,R}pk(I) );
|
||||
read_3(I,R, {nr}pk(R) );
|
||||
|
||||
claim_r1(R,Secret,ni);
|
||||
claim_r2(R,Secret,nr);
|
||||
claim_r3(R,Niagree);
|
||||
claim_r4(R,Nisynch);
|
||||
}
|
||||
}
|
||||
|
||||
// An untrusted agent, with leaked information
|
||||
|
||||
const Eve: Agent;
|
||||
untrusted Eve;
|
||||
compromised sk(Eve);
|
||||
|
BIN
gui/scyther-gui-16.ico
Normal file
BIN
gui/scyther-gui-16.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
gui/scyther-gui-32.ico
Normal file
BIN
gui/scyther-gui-32.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
BIN
gui/scyther-gui-64.ico
Normal file
BIN
gui/scyther-gui-64.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
77
gui/scyther-gui.py
Executable file
77
gui/scyther-gui.py
Executable file
@ -0,0 +1,77 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
""" Import externals """
|
||||
import wx
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
""" Import scyther-gui components """
|
||||
import Preference
|
||||
import Mainwindow
|
||||
import Misc
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class MySplashScreen(wx.SplashScreen):
|
||||
def __init__(self):
|
||||
bmp = wx.Image(Misc.mypath("images/scyther-splash.png")).ConvertToBitmap()
|
||||
wx.SplashScreen.__init__(self, bmp,
|
||||
wx.SPLASH_CENTRE_ON_SCREEN | wx.SPLASH_TIMEOUT,
|
||||
5000, None, -1)
|
||||
self.Bind(wx.EVT_CLOSE, self.OnClose)
|
||||
self.fc = wx.FutureCall(2000, self.ShowMain)
|
||||
|
||||
def OnClose(self, evt):
|
||||
# Make sure the default handler runs too so this window gets
|
||||
# destroyed
|
||||
evt.Skip()
|
||||
self.Hide()
|
||||
|
||||
# if the timer is still running then go ahead and show the
|
||||
# main frame now
|
||||
if self.fc.IsRunning():
|
||||
self.fc.Stop()
|
||||
self.ShowMain()
|
||||
|
||||
|
||||
def ShowMain(self):
|
||||
if self.fc.IsRunning():
|
||||
self.Raise()
|
||||
|
||||
|
||||
|
||||
class ScytherApp(wx.App):
|
||||
def OnInit(self):
|
||||
|
||||
"""
|
||||
Load preferences file
|
||||
"""
|
||||
|
||||
Preference.init()
|
||||
|
||||
"""
|
||||
Create and show the splash screen. It will then create and show
|
||||
the main frame when it is time to do so.
|
||||
"""
|
||||
|
||||
|
||||
splash = MySplashScreen()
|
||||
splash.Show()
|
||||
|
||||
""" Build up """
|
||||
self.mainWindow = Mainwindow.MainWindow('scythergui-default.spdl')
|
||||
self.SetTopWindow(self.mainWindow)
|
||||
self.mainWindow.Show()
|
||||
|
||||
return True
|
||||
|
||||
def OnExit(self):
|
||||
""" Tear down """
|
||||
|
||||
if __name__ == '__main__':
|
||||
scythergui = ScytherApp()
|
||||
scythergui.MainLoop()
|
||||
|
||||
|
51
gui/scythergui-default.spdl
Normal file
51
gui/scythergui-default.spdl
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Needham-Schroeder protocol
|
||||
*/
|
||||
|
||||
// PKI infrastructure
|
||||
|
||||
const pk: Function;
|
||||
secret sk: Function;
|
||||
inversekeys (pk,sk);
|
||||
|
||||
// The protocol description
|
||||
|
||||
protocol ns3(I,R)
|
||||
{
|
||||
role I
|
||||
{
|
||||
const ni: Nonce;
|
||||
var nr: Nonce;
|
||||
|
||||
send_1(I,R, {I,ni}pk(R) );
|
||||
read_2(R,I, {ni,nr}pk(I) );
|
||||
send_3(I,R, {nr}pk(R) );
|
||||
|
||||
claim_i1(I,Secret,ni);
|
||||
claim_i2(I,Secret,nr);
|
||||
claim_i3(I,Niagree);
|
||||
claim_i4(I,Nisynch);
|
||||
}
|
||||
|
||||
role R
|
||||
{
|
||||
var ni: Nonce;
|
||||
const nr: Nonce;
|
||||
|
||||
read_1(I,R, {I,ni}pk(R) );
|
||||
send_2(R,I, {ni,nr}pk(I) );
|
||||
read_3(I,R, {nr}pk(R) );
|
||||
|
||||
claim_r1(R,Secret,ni);
|
||||
claim_r2(R,Secret,nr);
|
||||
claim_r3(R,Niagree);
|
||||
claim_r4(R,Nisynch);
|
||||
}
|
||||
}
|
||||
|
||||
// An untrusted agent, with leaked information
|
||||
|
||||
const Eve: Agent;
|
||||
untrusted Eve;
|
||||
compromised sk(Eve);
|
||||
|
9
gui/todo.txt
Normal file
9
gui/todo.txt
Normal file
@ -0,0 +1,9 @@
|
||||
- Ideally we somehow color the correct/incorrect tags in the editor.
|
||||
- Line numbering is needed for the editor window otherwise you cannot
|
||||
interpret attacks.
|
||||
- Bottom bar should have obvious 'view' buttons for attacks that provide
|
||||
views, to guide the user.
|
||||
- Preferences window.
|
||||
- Save in local file on close.
|
||||
- Scyther executable should be able to be set by means of preferences.
|
||||
(Using a file select dialog)
|
Loading…
Reference in New Issue
Block a user