2006-08-02 13:59:57 +01:00
|
|
|
#!/usr/bin/python
|
2007-06-11 13:09:24 +01:00
|
|
|
"""
|
|
|
|
Scyther : An automatic verifier for security protocols.
|
2020-10-28 07:43:08 +00:00
|
|
|
Copyright (C) 2007-2020 Cas Cremers
|
2007-06-11 13:09:24 +01:00
|
|
|
|
|
|
|
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.
|
|
|
|
"""
|
|
|
|
|
2006-08-02 13:59:57 +01:00
|
|
|
#
|
|
|
|
# Scyther interface
|
|
|
|
#
|
|
|
|
|
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
""" Import externals """
|
|
|
|
import os
|
2006-08-07 11:42:34 +01:00
|
|
|
import os.path
|
2006-08-02 13:59:57 +01:00
|
|
|
import sys
|
2020-10-27 21:09:03 +00:00
|
|
|
import io
|
2006-12-11 09:40:57 +00:00
|
|
|
import tempfile
|
2023-11-08 15:04:19 +00:00
|
|
|
import platform
|
|
|
|
|
2010-11-09 11:05:18 +00:00
|
|
|
try:
|
|
|
|
import hashlib
|
|
|
|
HASHLIB = True
|
|
|
|
except ImportError:
|
|
|
|
HASHLIB = False
|
|
|
|
pass
|
2006-08-02 13:59:57 +01:00
|
|
|
|
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
""" Import scyther components """
|
2020-10-27 21:09:03 +00:00
|
|
|
from . import XMLReader
|
|
|
|
from . import Error
|
|
|
|
from . import Claim
|
|
|
|
from .Misc import *
|
2006-08-02 13:59:57 +01:00
|
|
|
|
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
2006-12-14 14:06:50 +00:00
|
|
|
"""
|
|
|
|
Globals
|
|
|
|
"""
|
|
|
|
|
|
|
|
FirstCheck = True
|
|
|
|
|
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
2006-11-23 11:28:51 +00:00
|
|
|
"""
|
2011-09-23 11:18:59 +01:00
|
|
|
Get current directory (for this file)
|
2006-11-23 11:28:51 +00:00
|
|
|
"""
|
2011-09-23 11:18:59 +01:00
|
|
|
def getMyDir():
|
|
|
|
return os.path.dirname( os.path.realpath( __file__ ) )
|
2006-08-09 10:26:15 +01:00
|
|
|
|
2011-09-23 11:18:59 +01:00
|
|
|
"""
|
|
|
|
The default path for the binaries is the current one.
|
|
|
|
"""
|
2006-08-09 12:54:37 +01:00
|
|
|
def getBinDir():
|
2011-09-23 11:18:59 +01:00
|
|
|
return getMyDir()
|
2006-08-09 12:54:37 +01:00
|
|
|
|
2011-09-23 11:18:59 +01:00
|
|
|
"""
|
|
|
|
Return Cache prefix path
|
2012-09-28 11:00:24 +01:00
|
|
|
Returns None if not existent
|
2011-09-23 11:18:59 +01:00
|
|
|
"""
|
|
|
|
def getCacheDir():
|
2012-09-28 11:00:24 +01:00
|
|
|
|
|
|
|
tmpdir = None
|
|
|
|
|
|
|
|
# Check if user chose the path
|
|
|
|
cachedirkey = "SCYTHERCACHEDIR"
|
2020-10-27 21:09:03 +00:00
|
|
|
if cachedirkey in list(os.environ.keys()):
|
2012-09-28 11:00:24 +01:00
|
|
|
tmpdir = os.environ[cachedirkey]
|
|
|
|
if tmpdir == "":
|
|
|
|
# Special value: if the variable is present, but equals the empty string, we disable caching.
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
# Otherwise take from path
|
|
|
|
tmpdir = tempfile.gettempdir()
|
|
|
|
|
|
|
|
# If not none, append special name
|
|
|
|
if tmpdir != None:
|
|
|
|
tmpdir = os.path.join(tmpdir,"Scyther-cache")
|
2023-01-22 21:22:11 +00:00
|
|
|
|
|
|
|
# Normalize the tmpdir path.
|
|
|
|
tmpdir = os.path.normpath(tmpdir)
|
2012-09-28 11:00:24 +01:00
|
|
|
|
|
|
|
return tmpdir
|
|
|
|
|
|
|
|
|
2006-08-09 12:54:37 +01:00
|
|
|
|
2006-08-09 10:26:15 +01:00
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
2006-12-14 14:06:50 +00:00
|
|
|
def Check():
|
|
|
|
"""
|
|
|
|
Various dynamic checks that can be performed before starting the
|
|
|
|
backend.
|
|
|
|
"""
|
|
|
|
|
|
|
|
global FirstCheck
|
|
|
|
|
|
|
|
# First time
|
|
|
|
if FirstCheck:
|
|
|
|
"""
|
|
|
|
Perform any checks that only need to be done the first time.
|
|
|
|
"""
|
|
|
|
FirstCheck = False
|
|
|
|
|
|
|
|
# Every time
|
|
|
|
|
|
|
|
# Check Scyther backend program availability
|
|
|
|
program = getScytherBackend()
|
|
|
|
CheckSanity(program)
|
|
|
|
|
2007-05-18 13:06:29 +01:00
|
|
|
|
2006-12-14 14:06:50 +00:00
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
def CheckSanity(program):
|
|
|
|
"""
|
|
|
|
This is where the existence is checked of the Scyther backend.
|
|
|
|
"""
|
|
|
|
|
|
|
|
if not os.path.isfile(program):
|
2020-10-27 21:09:03 +00:00
|
|
|
raise Error.BinaryError(program)
|
2006-12-14 14:06:50 +00:00
|
|
|
|
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
2007-01-27 12:53:19 +00:00
|
|
|
def EnsureString(x,sep=" "):
|
|
|
|
"""
|
|
|
|
Takes a thing that is either a list or a string.
|
|
|
|
Turns it into a string. If it was a list, <sep> is inserted, and the
|
|
|
|
process iterats.
|
2007-05-18 13:06:29 +01:00
|
|
|
|
|
|
|
TODO does not accept unicode yet, that is something that must be
|
|
|
|
handled to or we run into wxPython problems eventually.
|
2007-01-27 12:53:19 +00:00
|
|
|
"""
|
|
|
|
if type(x) is str:
|
|
|
|
return x
|
|
|
|
|
|
|
|
elif type(x) is list:
|
|
|
|
newlist = []
|
|
|
|
for el in x:
|
|
|
|
newlist.append(EnsureString(el,sep))
|
|
|
|
return sep.join(newlist)
|
|
|
|
|
|
|
|
else:
|
2020-10-27 21:09:03 +00:00
|
|
|
raise Error.StringListError(x)
|
2007-01-27 12:53:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
2006-11-23 11:28:51 +00:00
|
|
|
def getScytherBackend():
|
|
|
|
# Where is my executable?
|
|
|
|
#
|
|
|
|
# Auto-detect platform and infer executable name from that
|
|
|
|
#
|
|
|
|
if "linux" in sys.platform:
|
|
|
|
|
|
|
|
""" linux """
|
|
|
|
scythername = "scyther-linux"
|
|
|
|
|
2006-12-20 17:20:09 +00:00
|
|
|
elif "darwin" in sys.platform:
|
2006-08-04 23:00:22 +01:00
|
|
|
|
2006-12-20 17:20:09 +00:00
|
|
|
""" OS X """
|
2023-11-08 15:04:19 +00:00
|
|
|
# Check if there is an ARM version available at scyther-mac-arm
|
|
|
|
# Otherwise, just fallback to the default scyther-mac which is the
|
|
|
|
# Intel version for backwards-compatibility reasons.
|
2023-11-08 17:27:46 +00:00
|
|
|
has_arm_build = os.path.exists(os.path.join(getBinDir(),"scyther-mac-arm"))
|
|
|
|
|
|
|
|
if platform.processor().startswith("arm") and has_arm_build:
|
2023-11-08 15:04:19 +00:00
|
|
|
scythername = "scyther-mac-arm"
|
|
|
|
else:
|
|
|
|
scythername = "scyther-mac"
|
2006-09-21 14:19:22 +01:00
|
|
|
|
2006-11-23 11:28:51 +00:00
|
|
|
elif sys.platform.startswith('win'):
|
2006-09-21 14:19:22 +01:00
|
|
|
|
2006-11-23 11:28:51 +00:00
|
|
|
""" Windows """
|
2006-11-23 11:46:33 +00:00
|
|
|
scythername = "scyther-w32.exe"
|
2006-09-21 14:19:22 +01:00
|
|
|
|
2006-11-23 11:28:51 +00:00
|
|
|
else:
|
2006-09-21 14:19:22 +01:00
|
|
|
|
2006-11-23 11:28:51 +00:00
|
|
|
""" Unsupported"""
|
2020-10-27 21:09:03 +00:00
|
|
|
raise Error.UnknownPlatformError(sys.platform)
|
2006-09-21 14:19:22 +01:00
|
|
|
|
2006-11-23 11:28:51 +00:00
|
|
|
program = os.path.join(getBinDir(),scythername)
|
|
|
|
return program
|
|
|
|
|
2006-12-14 19:46:36 +00:00
|
|
|
|
2006-11-23 11:28:51 +00:00
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
class Scyther(object):
|
|
|
|
def __init__ ( self):
|
2006-08-04 23:00:22 +01:00
|
|
|
|
2006-08-08 16:54:00 +01:00
|
|
|
# Init
|
2006-11-23 11:28:51 +00:00
|
|
|
self.program = getScytherBackend()
|
2006-08-02 13:59:57 +01:00
|
|
|
self.spdl = None
|
2006-08-02 14:44:45 +01:00
|
|
|
self.inputfile = None
|
2010-11-09 10:09:31 +00:00
|
|
|
self.filenames = []
|
2006-08-08 17:16:28 +01:00
|
|
|
self.options = ""
|
2006-08-02 13:59:57 +01:00
|
|
|
self.claims = None
|
2006-08-06 20:52:07 +01:00
|
|
|
self.errors = None
|
2006-08-06 22:16:14 +01:00
|
|
|
self.errorcount = 0
|
2007-01-12 10:02:56 +00:00
|
|
|
self.warnings = None
|
2006-08-07 11:52:48 +01:00
|
|
|
self.run = False
|
2006-08-08 16:54:00 +01:00
|
|
|
self.output = None
|
2007-01-02 15:22:25 +00:00
|
|
|
self.cmd = None
|
2006-08-08 16:54:00 +01:00
|
|
|
|
|
|
|
# defaults
|
|
|
|
self.xml = True # this results in a claim end, otherwise we simply get the output
|
2006-08-02 13:59:57 +01:00
|
|
|
|
|
|
|
def setInput(self,spdl):
|
|
|
|
self.spdl = spdl
|
2006-08-02 14:44:45 +01:00
|
|
|
self.inputfile = None
|
2010-11-09 10:09:31 +00:00
|
|
|
self.guessFileNames()
|
2006-08-02 13:59:57 +01:00
|
|
|
|
|
|
|
def setFile(self,filename):
|
2006-08-02 14:44:45 +01:00
|
|
|
self.inputfile = filename
|
2010-11-09 10:09:31 +00:00
|
|
|
self.filenames = [self.inputfile]
|
2006-08-02 13:59:57 +01:00
|
|
|
self.spdl = ""
|
|
|
|
fp = open(filename,"r")
|
|
|
|
for l in fp.readlines():
|
|
|
|
self.spdl += l
|
|
|
|
fp.close()
|
|
|
|
|
2006-08-07 19:23:30 +01:00
|
|
|
def addFile(self,filename):
|
|
|
|
self.inputfile = None
|
|
|
|
if not self.spdl:
|
|
|
|
self.spdl = ""
|
|
|
|
fp = open(filename,"r")
|
|
|
|
for l in fp.readlines():
|
|
|
|
self.spdl += l
|
|
|
|
fp.close()
|
2010-11-09 10:09:31 +00:00
|
|
|
self.guessFileNames()
|
|
|
|
|
|
|
|
def guessFileNames(self,spdl=None):
|
|
|
|
"""
|
|
|
|
Try to extract filenames (well, actually, protocol names) sloppily from some spdl script.
|
|
|
|
|
|
|
|
There are two modes:
|
|
|
|
|
|
|
|
[init] : If the spdl parameter is empty or None, we reset the filenames and extract from self.spdl
|
|
|
|
[add] : If the spdl parameter is non-empty, add the extracted filenames to an existing list
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
if (spdl == None) or (len(spdl) == 0):
|
|
|
|
spdl = self.spdl
|
2010-12-31 14:42:01 +00:00
|
|
|
if spdl == None:
|
|
|
|
spdl = ""
|
2010-11-09 10:09:31 +00:00
|
|
|
self.filenames = []
|
|
|
|
|
|
|
|
for sl in spdl.splitlines():
|
|
|
|
l = sl.strip()
|
|
|
|
prefix = "protocol "
|
|
|
|
postfix = "("
|
|
|
|
x = l.find(prefix)
|
|
|
|
if x >= 0:
|
|
|
|
# The prefix occurs
|
|
|
|
y = l.find(postfix,x+len(prefix))
|
|
|
|
if y >= 0:
|
|
|
|
gn = l[x+len(prefix):y]
|
|
|
|
# check for helper protocols
|
|
|
|
if not gn.startswith("@"):
|
|
|
|
if gn not in self.filenames:
|
|
|
|
self.filenames.append(gn)
|
2006-08-07 19:23:30 +01:00
|
|
|
|
2007-01-02 15:22:25 +00:00
|
|
|
def addArglist(self,arglist):
|
|
|
|
for arg in arglist:
|
|
|
|
self.options += " %s" % (arg)
|
|
|
|
|
2013-06-30 22:14:28 +01:00
|
|
|
def doScytherCommand(self, spdl, args, checkKnown=False, storePopen=None):
|
2010-11-09 11:05:18 +00:00
|
|
|
"""
|
|
|
|
Cached version of the 'real' below
|
2012-09-28 11:00:24 +01:00
|
|
|
|
|
|
|
TODO: CC: One possible problem with the caching is the side-effect, e.g., scyther writing to specific named output files. These are not
|
|
|
|
captured in the cache. I don't have a good solution for that yet.
|
2010-11-09 11:05:18 +00:00
|
|
|
"""
|
|
|
|
global HASHLIB
|
|
|
|
|
2012-09-28 11:00:24 +01:00
|
|
|
# Can we use the cache?
|
|
|
|
canCache = False
|
|
|
|
if HASHLIB:
|
|
|
|
cacheDir = getCacheDir()
|
|
|
|
if cacheDir != None:
|
|
|
|
canCache = True
|
|
|
|
else:
|
|
|
|
cacheDir = None
|
|
|
|
|
|
|
|
# If we cannot use the cache, we either need to compute or, if checking for cache presense,...
|
|
|
|
if not canCache:
|
2011-03-31 14:02:49 +01:00
|
|
|
if checkKnown == True:
|
2012-09-28 11:00:24 +01:00
|
|
|
# not using the cache, so we don't have it already
|
2011-03-31 14:02:49 +01:00
|
|
|
return False
|
|
|
|
else:
|
|
|
|
# Need to compute
|
2013-06-30 22:14:28 +01:00
|
|
|
return self.doScytherCommandReal(spdl,args, storePopen=storePopen)
|
2010-11-09 11:05:18 +00:00
|
|
|
|
2012-09-28 11:00:24 +01:00
|
|
|
# Apparently we are supporsed to be able to use the cache
|
2010-11-09 11:05:18 +00:00
|
|
|
m = hashlib.sha256()
|
2020-10-27 21:10:55 +00:00
|
|
|
|
|
|
|
def muppet(m, s):
|
|
|
|
m.update(s.encode('utf-8'))
|
|
|
|
|
2010-11-09 11:05:18 +00:00
|
|
|
if spdl == None:
|
2020-10-27 21:10:55 +00:00
|
|
|
muppet(m, "[spdl:None]")
|
2010-11-09 11:05:18 +00:00
|
|
|
else:
|
2020-10-27 21:10:55 +00:00
|
|
|
muppet(m, spdl)
|
2010-11-09 11:05:18 +00:00
|
|
|
if args == None:
|
2020-10-27 21:10:55 +00:00
|
|
|
muppet(m, "[args:None]")
|
2010-11-09 11:05:18 +00:00
|
|
|
else:
|
2020-10-27 21:10:55 +00:00
|
|
|
muppet(m, args)
|
2010-11-09 11:05:18 +00:00
|
|
|
|
|
|
|
uid = m.hexdigest()
|
|
|
|
|
2011-01-21 16:38:45 +00:00
|
|
|
# Split the uid to make 256 subdirectories with 256 subdirectories...
|
2010-11-09 11:05:18 +00:00
|
|
|
prefixlen = 2
|
|
|
|
uid1 = uid[:prefixlen]
|
2011-01-21 16:38:45 +00:00
|
|
|
uid2 = uid[prefixlen:prefixlen+2]
|
|
|
|
uid3 = uid[prefixlen+2:]
|
2010-11-09 11:05:18 +00:00
|
|
|
|
|
|
|
# Possibly we could also decide to store input and arguments in the cache to analyze things later
|
|
|
|
|
2011-09-23 11:18:59 +01:00
|
|
|
# Construct: cachePath/uid1/uid2/...
|
2012-09-28 11:00:24 +01:00
|
|
|
path = os.path.join(cacheDir,uid1,uid2)
|
2011-01-21 16:38:45 +00:00
|
|
|
name1 = "%s.out" % (uid3)
|
|
|
|
name2 = "%s.err" % (uid3)
|
2010-11-09 11:05:18 +00:00
|
|
|
|
2011-09-23 11:18:59 +01:00
|
|
|
fname1 = os.path.join(path, name1)
|
|
|
|
fname2 = os.path.join(path, name2)
|
2010-11-09 11:05:18 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
"""
|
|
|
|
Try to retrieve the result from the cache
|
|
|
|
"""
|
|
|
|
fh1 = open(fname1,"r")
|
|
|
|
out = fh1.read()
|
|
|
|
fh1.close()
|
|
|
|
fh2 = open(fname2,"r")
|
|
|
|
err = fh2.read()
|
|
|
|
fh2.close()
|
2011-03-31 14:02:49 +01:00
|
|
|
if checkKnown == True:
|
|
|
|
# We got to here, so we have it
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
# Not checking cache, we need the result
|
|
|
|
return (out,err)
|
2010-11-09 11:05:18 +00:00
|
|
|
except:
|
2012-09-28 11:00:24 +01:00
|
|
|
pass
|
2011-03-31 14:02:49 +01:00
|
|
|
|
2012-09-28 11:00:24 +01:00
|
|
|
"""
|
|
|
|
Something went wrong, do the real thing and cache afterwards
|
|
|
|
"""
|
|
|
|
if checkKnown == True:
|
|
|
|
# We were only checking, abort
|
|
|
|
return False
|
|
|
|
|
2013-06-30 22:14:28 +01:00
|
|
|
(out,err) = self.doScytherCommandReal(spdl,args, storePopen=storePopen)
|
2010-11-09 11:05:18 +00:00
|
|
|
|
2012-09-28 11:00:24 +01:00
|
|
|
try:
|
|
|
|
# Try to store result in cache
|
2010-11-09 11:05:18 +00:00
|
|
|
ensurePath(path)
|
|
|
|
|
|
|
|
fh1 = open(fname1,"w")
|
|
|
|
fh1.write(out)
|
|
|
|
fh1.close()
|
|
|
|
|
|
|
|
fh2 = open(fname2,"w")
|
|
|
|
fh2.write(err)
|
|
|
|
fh2.close()
|
2012-09-28 11:00:24 +01:00
|
|
|
except:
|
|
|
|
pass
|
2010-11-09 11:05:18 +00:00
|
|
|
|
2012-09-28 11:00:24 +01:00
|
|
|
return (out,err)
|
2010-11-09 11:05:18 +00:00
|
|
|
|
|
|
|
|
2013-06-30 22:14:28 +01:00
|
|
|
def doScytherCommandReal(self, spdl, args, storePopen=None):
|
2006-12-14 19:46:36 +00:00
|
|
|
"""
|
|
|
|
Run Scyther backend on the input
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
spdl -- string describing the spdl text
|
|
|
|
args -- arguments for the command-line
|
2013-06-30 22:14:28 +01:00
|
|
|
storePopen -- callback function to register Popen objects (used for process kill by other threads)
|
2006-12-14 19:46:36 +00:00
|
|
|
Returns:
|
|
|
|
(output,errors)
|
|
|
|
output -- string which is the real output
|
|
|
|
errors -- string which captures the errors
|
|
|
|
"""
|
2006-11-23 11:28:51 +00:00
|
|
|
|
|
|
|
if self.program == None:
|
2006-12-14 19:46:36 +00:00
|
|
|
raise Error.NoBinaryError
|
2006-08-02 13:59:57 +01:00
|
|
|
|
2006-12-14 20:32:24 +00:00
|
|
|
# Sanitize input somewhat
|
2007-01-27 21:42:16 +00:00
|
|
|
if spdl == "":
|
2006-12-14 20:32:24 +00:00
|
|
|
# Scyther hickups on completely empty input
|
2007-01-27 21:42:16 +00:00
|
|
|
spdl = "\n"
|
2006-12-14 20:32:24 +00:00
|
|
|
|
2010-11-09 10:09:31 +00:00
|
|
|
# Extract filenames for error reporting later
|
|
|
|
self.guessFileNames(spdl=spdl)
|
|
|
|
|
2007-01-02 15:34:58 +00:00
|
|
|
# Generate temporary files for the output.
|
2006-12-14 20:08:34 +00:00
|
|
|
(fde,fne) = tempfile.mkstemp() # errors
|
|
|
|
(fdo,fno) = tempfile.mkstemp() # output
|
2023-01-22 21:22:11 +00:00
|
|
|
|
|
|
|
# Normalize the temporary paths.
|
|
|
|
fne = os.path.normpath(fne)
|
|
|
|
fno = os.path.normpath(fno)
|
|
|
|
|
2007-01-02 15:22:25 +00:00
|
|
|
if spdl:
|
|
|
|
(fdi,fni) = tempfile.mkstemp() # input
|
2023-01-22 21:22:11 +00:00
|
|
|
fni = os.path.normpath(fni)
|
2006-12-14 20:08:34 +00:00
|
|
|
|
2007-01-02 15:22:25 +00:00
|
|
|
# Write (input) file
|
2020-10-27 21:10:55 +00:00
|
|
|
fhi = os.fdopen(fdi,'w+')
|
2007-01-02 15:22:25 +00:00
|
|
|
fhi.write(spdl)
|
|
|
|
fhi.close()
|
2006-12-11 09:40:57 +00:00
|
|
|
|
|
|
|
# Generate command line for the Scyther process
|
2006-12-14 19:46:36 +00:00
|
|
|
self.cmd = ""
|
2010-05-15 23:36:06 +01:00
|
|
|
self.cmd += "\"%s\"" % self.program
|
2023-01-22 21:22:11 +00:00
|
|
|
self.cmd += " --append-errors=\"%s\"" % fne
|
|
|
|
self.cmd += " --append-output=\"%s\"" % fno
|
2007-01-02 15:22:25 +00:00
|
|
|
self.cmd += " %s" % args
|
2006-12-14 20:08:34 +00:00
|
|
|
if spdl:
|
2023-01-22 21:22:11 +00:00
|
|
|
self.cmd += " \"%s\"" % fni
|
2006-12-14 20:08:34 +00:00
|
|
|
|
2007-01-02 15:34:58 +00:00
|
|
|
# Only for debugging, really
|
2023-01-22 21:22:11 +00:00
|
|
|
# print( self.cmd )
|
2007-01-02 15:22:25 +00:00
|
|
|
|
2006-12-14 20:08:34 +00:00
|
|
|
# Start the process
|
2013-06-30 22:14:28 +01:00
|
|
|
safeCommand(self.cmd, storePopen=storePopen)
|
2006-12-14 20:08:34 +00:00
|
|
|
|
|
|
|
# reseek
|
|
|
|
fhe = os.fdopen(fde)
|
|
|
|
fho = os.fdopen(fdo)
|
|
|
|
errors = fhe.read()
|
|
|
|
output = fho.read()
|
|
|
|
|
|
|
|
# clean up files
|
|
|
|
fhe.close()
|
|
|
|
fho.close()
|
|
|
|
os.remove(fne)
|
|
|
|
os.remove(fno)
|
2007-01-02 15:22:25 +00:00
|
|
|
if spdl:
|
|
|
|
os.remove(fni)
|
2006-12-14 19:46:36 +00:00
|
|
|
|
|
|
|
return (output,errors)
|
|
|
|
|
2007-01-27 12:53:19 +00:00
|
|
|
def sanitize(self):
|
|
|
|
""" Sanitize some of the input """
|
|
|
|
self.options = EnsureString(self.options)
|
|
|
|
|
2013-06-30 22:14:28 +01:00
|
|
|
def verify(self,extraoptions=None,checkKnown=False,storePopen=None):
|
2006-12-14 19:46:36 +00:00
|
|
|
""" Should return a list of results """
|
2011-03-31 14:02:49 +01:00
|
|
|
""" If checkKnown == True, we do not call Scyther, but just check the cache, and return True iff the result is in the cache """
|
2007-01-27 12:53:19 +00:00
|
|
|
|
|
|
|
# Cleanup first
|
|
|
|
self.sanitize()
|
2006-12-14 19:46:36 +00:00
|
|
|
|
|
|
|
# prepare arguments
|
|
|
|
args = ""
|
|
|
|
if self.xml:
|
|
|
|
args += " --dot-output --xml-output --plain"
|
|
|
|
args += " %s" % self.options
|
2007-01-27 12:53:19 +00:00
|
|
|
if extraoptions:
|
|
|
|
# extraoptions might need sanitizing
|
|
|
|
args += " %s" % EnsureString(extraoptions)
|
2006-12-14 19:46:36 +00:00
|
|
|
|
2011-03-31 14:02:49 +01:00
|
|
|
# Are we only checking the cache?
|
|
|
|
if checkKnown == True:
|
2013-06-30 22:14:28 +01:00
|
|
|
return self.doScytherCommand(self.spdl, args, checkKnown=checkKnown, storePopen=storePopen)
|
2011-03-31 14:02:49 +01:00
|
|
|
|
2006-12-14 19:46:36 +00:00
|
|
|
# execute
|
2013-06-30 22:14:28 +01:00
|
|
|
(output,errors) = self.doScytherCommand(self.spdl, args, storePopen=storePopen)
|
2006-12-14 19:46:36 +00:00
|
|
|
self.run = True
|
|
|
|
|
|
|
|
# process errors
|
|
|
|
self.errors = []
|
2007-01-12 10:02:56 +00:00
|
|
|
self.warnings = []
|
2006-12-14 19:46:36 +00:00
|
|
|
for l in errors.splitlines():
|
2007-01-12 10:02:56 +00:00
|
|
|
line = l.strip()
|
2007-01-27 21:42:16 +00:00
|
|
|
if len(line) > 0:
|
|
|
|
# filter out any non-errors (say maybe only claim etc) and count
|
|
|
|
# them.
|
|
|
|
if line.startswith("claim\t"):
|
|
|
|
# Claims are lost, reconstructed from the XML output
|
2007-01-30 17:43:58 +00:00
|
|
|
pass
|
|
|
|
elif line.startswith("warning"):
|
2007-01-27 21:42:16 +00:00
|
|
|
# Warnings are stored seperately
|
|
|
|
self.warnings.append(line)
|
2007-01-30 17:43:58 +00:00
|
|
|
else:
|
|
|
|
# otherwise it is an error
|
|
|
|
self.errors.append(line)
|
2007-01-12 10:02:56 +00:00
|
|
|
|
2006-08-06 22:16:14 +01:00
|
|
|
self.errorcount = len(self.errors)
|
2007-01-27 21:42:16 +00:00
|
|
|
if self.errorcount > 0:
|
2010-11-09 10:09:31 +00:00
|
|
|
raise Error.ScytherError(self.errors,filenames=self.filenames,options=self.options)
|
2006-08-06 20:52:07 +01:00
|
|
|
|
2006-12-14 19:46:36 +00:00
|
|
|
# process output
|
|
|
|
self.output = output
|
|
|
|
self.validxml = False
|
|
|
|
self.claims = []
|
2006-08-08 16:54:00 +01:00
|
|
|
if self.xml:
|
|
|
|
if len(output) > 0:
|
2006-08-08 17:16:28 +01:00
|
|
|
if output.startswith("<scyther>"):
|
2006-12-14 19:46:36 +00:00
|
|
|
|
|
|
|
# whoohee, xml
|
2006-08-08 17:16:28 +01:00
|
|
|
self.validxml = True
|
|
|
|
|
2020-10-27 21:10:55 +00:00
|
|
|
xmlfile = io.StringIO(output)
|
2006-12-14 19:46:36 +00:00
|
|
|
reader = XMLReader.XMLReader()
|
|
|
|
self.claims = reader.readXML(xmlfile)
|
2006-08-02 13:59:57 +01:00
|
|
|
|
2006-12-14 19:46:36 +00:00
|
|
|
# Determine what should be the result
|
|
|
|
if self.xml:
|
|
|
|
return self.claims
|
|
|
|
else:
|
|
|
|
return self.output
|
2006-08-02 13:59:57 +01:00
|
|
|
|
2013-06-30 22:14:28 +01:00
|
|
|
def verifyOne(self,cl=None,checkKnown=False,storePopen=None):
|
2007-01-27 12:53:19 +00:00
|
|
|
"""
|
|
|
|
Verify just a single claim with an ID retrieved from the
|
2007-01-27 13:08:24 +00:00
|
|
|
procedure below, 'scanClaims', or a full claim object
|
2011-03-31 14:02:49 +01:00
|
|
|
|
|
|
|
If checkKnown is True, return if the result is already known (but never recompute).
|
2007-01-27 12:53:19 +00:00
|
|
|
"""
|
2007-01-27 21:42:16 +00:00
|
|
|
if cl:
|
|
|
|
# We accept either a claim or a claim id
|
|
|
|
if isinstance(cl,Claim.Claim):
|
|
|
|
cl = cl.id
|
2013-06-30 22:14:28 +01:00
|
|
|
return self.verify("--filter=%s" % cl, checkKnown=checkKnown,storePopen=storePopen)
|
2007-01-27 21:42:16 +00:00
|
|
|
else:
|
|
|
|
# If no claim, then its just normal verification
|
2013-06-30 22:14:28 +01:00
|
|
|
return self.verify(checkKnown=checkKnown,storePopen=storePopen)
|
2007-01-27 12:53:19 +00:00
|
|
|
|
|
|
|
def scanClaims(self):
|
|
|
|
"""
|
2007-01-27 13:08:24 +00:00
|
|
|
Retrieve the list of claims. Of each element (a claim), claim.id
|
|
|
|
can be passed to --filter=X or 'verifyOne' later.
|
2007-01-27 12:53:19 +00:00
|
|
|
A result of 'None' means that some errors occurred.
|
|
|
|
"""
|
|
|
|
self.verify("--scan-claims")
|
|
|
|
if self.errorcount > 0:
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
self.validxml = False # Signal that we should not interpret the output as XML
|
2007-01-27 13:08:24 +00:00
|
|
|
return self.claims
|
2007-01-27 12:53:19 +00:00
|
|
|
|
2006-08-07 19:23:30 +01:00
|
|
|
def getClaim(self,claimid):
|
|
|
|
if self.claims:
|
|
|
|
for cl in self.claims:
|
|
|
|
if cl.id == claimid:
|
|
|
|
return cl
|
|
|
|
return None
|
|
|
|
|
2006-08-02 13:59:57 +01:00
|
|
|
def __str__(self):
|
2006-08-07 11:52:48 +01:00
|
|
|
if self.run:
|
|
|
|
if self.errorcount > 0:
|
2006-08-07 17:40:46 +01:00
|
|
|
return "%i errors:\n%s" % (self.errorcount, "\n".join(self.errors))
|
2006-08-07 11:52:48 +01:00
|
|
|
else:
|
2006-08-08 17:16:28 +01:00
|
|
|
if self.xml and self.validxml:
|
|
|
|
s = "Verification results:\n"
|
2006-08-08 16:54:00 +01:00
|
|
|
for cl in self.claims:
|
|
|
|
s += str(cl) + "\n"
|
|
|
|
return s
|
|
|
|
else:
|
|
|
|
return self.output
|
2006-08-02 13:59:57 +01:00
|
|
|
else:
|
|
|
|
return "Scyther has not been run yet."
|
|
|
|
|
2007-05-18 13:06:29 +01:00
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
2011-03-31 14:02:49 +01:00
|
|
|
def GetClaims(filelist, filterlist=None):
|
|
|
|
"""
|
|
|
|
Given a list of file names in filelist,
|
|
|
|
returns a dictionary of filenames to lists claim names.
|
|
|
|
Filenames which yielded no claims are filtered out.
|
|
|
|
|
|
|
|
Filterlist may be None or a list of claim names (Secret, SKR, Niagree etc).
|
|
|
|
"""
|
|
|
|
|
|
|
|
dict = {}
|
|
|
|
for fname in filelist:
|
|
|
|
try:
|
|
|
|
sc = Scyther()
|
|
|
|
sc.setFile(fname)
|
|
|
|
l = sc.scanClaims()
|
|
|
|
if l != None:
|
|
|
|
cl = []
|
|
|
|
for claim in l:
|
|
|
|
if filterlist == None:
|
|
|
|
cl.append(claim.id)
|
|
|
|
else:
|
|
|
|
if claim.claimtype in filterlist:
|
|
|
|
cl.append(claim.id)
|
|
|
|
dict[fname] = cl
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
return dict
|
|
|
|
|
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
def FindProtocols(path="",filterProtocol=None):
|
|
|
|
"""
|
|
|
|
Find a list of protocol names
|
|
|
|
|
|
|
|
Note: Unix only! Will not work under windows.
|
|
|
|
"""
|
|
|
|
|
2020-10-27 21:10:55 +00:00
|
|
|
import subprocess
|
2011-03-31 14:02:49 +01:00
|
|
|
|
|
|
|
cmd = "find %s -iname '*.spdl'" % (path)
|
2020-10-27 21:10:55 +00:00
|
|
|
plist = subprocess.getoutput(cmd).splitlines()
|
2011-03-31 14:02:49 +01:00
|
|
|
nlist = []
|
|
|
|
for prot in plist:
|
|
|
|
if filterProtocol != None:
|
|
|
|
if filterProtocol(prot):
|
|
|
|
nlist.append(prot)
|
|
|
|
else:
|
|
|
|
nlist.append(prot)
|
|
|
|
return nlist
|
|
|
|
|
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
2007-05-18 13:06:29 +01:00
|
|
|
def GetInfo(html=False):
|
|
|
|
"""
|
|
|
|
Retrieve a tuple (location,string) with information about the tool,
|
|
|
|
retrieved from the --expert --version data
|
|
|
|
"""
|
|
|
|
|
|
|
|
program = getScytherBackend()
|
|
|
|
arg = "--expert --version"
|
|
|
|
sc = Scyther()
|
|
|
|
(output,errors) = sc.doScytherCommand(spdl=None, args=arg)
|
|
|
|
if not html:
|
|
|
|
return (program,output)
|
|
|
|
else:
|
|
|
|
sep = "<br>\n"
|
|
|
|
html = "Backend: %s%s" % (program,sep)
|
|
|
|
for l in output.splitlines():
|
|
|
|
l.strip()
|
|
|
|
html += "%s%s" % (l,sep)
|
|
|
|
return html
|
|
|
|
|
|
|
|
|
2007-10-08 13:52:50 +01:00
|
|
|
def GetLicense():
|
|
|
|
"""
|
|
|
|
Retrieve license information.
|
|
|
|
"""
|
|
|
|
|
|
|
|
program = getScytherBackend()
|
|
|
|
arg = "--license"
|
|
|
|
sc = Scyther()
|
|
|
|
(output,errors) = sc.doScytherCommand(spdl=None, args=arg)
|
|
|
|
return output
|
|
|
|
|
|
|
|
|
2007-05-18 13:06:29 +01:00
|
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
2006-12-14 19:46:36 +00:00
|
|
|
# vim: set ts=4 sw=4 et list lcs=tab\:>-:
|