# # Copyright 2023 United States Government # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # import os import re import sys from xml.dom import minidom from xml.parsers.expat import ExpatError class meas(object): def __init__(self): self.name = 'none' self.desc = '' self.uuid = '' self.measfile = '' self.instructions = { "instruction":[]} self.variables = { "variable":[]} def printHeader( ): "This Function Prints SPHINX Header to Standard Output" print ('Maat Measurement Specifications') print ('===============================\n') print ('.. currentmodule:: maat') print ('') print ('Measurement Specifications define exactly what evidence' + ' the requester requires for a specific scenario. Separating the' + ' evidence requirements from the protocol needed to collect and' + ' transmit evidence (APBs) allows the construction of generic' + ' APBs that can be re-used for multiple attestation scenarios.' + ' Like APBs, Measurement Specifications are registered with' + ' the AM and are identified by a well-known UUID.') print ('') return def printMeasInfo(meas): "Print Meas Info for each Type" print ('.. _' + meas.uuid + ':\n\n' + meas.name) print ('-----------------------------------\n') print('| UUID: ' + meas.uuid) print('| Description: \n| \t' + meas.desc) print('| Instructions:') for values in iter(meas.instructions.values()): for value in values: print("|\tName: " + value["name"] + ", Type: " + value["type"]) if (value["target_type_name"] != ''): print("""|\t\tTarget Type: :ref:`""" + value["target_type_name"] + """ <""" + "Magic" + value["target_type_magic"] + """>`""") if (value["address_type_name"] != ''): print("""|\t\tAddress Type: :ref:`""" + value["address_type_name"] + """ <""" + "Magic" + value["address_type_magic"] + """>`""") if (value["meas_type_name"] != ''): print("""|\t\tMeasurement Type: :ref:`""" + value["meas_type_name"] + """ <""" + "Magic" + value["meas_type_magic"] + """>`""") if (value["action_feature"] != ''): print("|\t\t" + "Feature: " + value["action_feature"] + ", Instruction: " + value["action_instruction"]) print('') print('| Variables:') for values in iter(meas.variables.values()): for value in values: print("|\t Instruction:" + value["instruction"] + ", Scope: " + value["scope"]) for addr,opr in value["address"]: print("|\t\tAddress: " + addr + ", Operation: " + opr) print('') return def parseMeasXml( string ): "Parse Meas Spec XML" doc = parseXml(string); if (doc is not None): parsedmeas = parseMeasFields(doc) return parsedmeas return None def parseXml( path ): "minidom xml parsing" try: doc = minidom.parse(path) return doc except IOError as e: print("I/O error ({0}): {1}".format(e.errno, e.strerror)) return None except ExpatError as expatError: print("Minidom Parse Error: " + str(expatError) + " when parsing " + path) return None except: print("Unexpected error: " + sys.exc_info()[0]) return None def parseMeasFields(doc): parsedmeas = meas(); parsedmeas = parseNameDesc(doc, parsedmeas); if (parsedmeas is not None): parsedmeas = parseInstr(doc, parsedmeas); if (parsedmeas is not None): parsedmeas = parseVar(doc, parsedmeas); return parsedmeas; def parseNameDesc(doc, parsedmeas): try: field = doc.getElementsByTagName("name")[0] name = re.sub('measurement_specification', '', field.firstChild.data) parsedmeas.name = re.sub('measurement specification', '', name) field = doc.getElementsByTagName("uuid")[0] parsedmeas.uuid = field.firstChild.data field = doc.getElementsByTagName("description")[0] parsedmeas.desc = field.firstChild.data return parsedmeas; except: print("Mesaurement Specification Error: " + sys.exc_info()[0]) return None def parseInstr(doc, parsedmeas): try: instructions = doc.getElementsByTagName("instruction") # there can be multiple instructions, but each instruction can only have a single # target type, address type, measurement type, action type # if there are multiple, the parsed instruction will use the last instance for instruction in instructions: entry = {} entry["type"] = instruction.getAttribute("type") entry["name"] = instruction.getAttribute("name") entry["target_type_name"] = "" entry["target_type_magic"] = "" targettypes = instruction.getElementsByTagName("target_type") for targettype in targettypes: entry["target_type_name"] = targettype.getAttribute("name") entry["target_type_magic"] = targettype.getAttribute("magic") entry["address_type_name"] = "" entry["address_type_magic"] = "" addresstypes = instruction.getElementsByTagName("address_type") for addresstype in addresstypes: entry["address_type_name"] = addresstype.getAttribute("name") entry["address_type_magic"] = addresstype.getAttribute("magic") entry["meas_type_name"] = "" entry["meas_type_magic"] = "" meastypes = instruction.getElementsByTagName("measurement_type") for meastype in meastypes: entry["meas_type_name"] = meastype.getAttribute("name") entry["meas_type_magic"] = meastype.getAttribute("magic") entry["action_feature"] = "" entry["action_instruction"] = "" actiontypes = instruction.getElementsByTagName("action") for actiontype in actiontypes: entry["action_feature"] = actiontype.getAttribute("feature") entry["action_instruction"] = actiontype.getAttribute("instruction") parsedmeas.instructions["instruction"].append(entry) return parsedmeas; except: print("Unexpected error: " + sys.exc_info()[0]) return None; def parseVar(doc, parsedmeas): try: variables = doc.getElementsByTagName("variable") for variable in variables: entry = {} entry["instruction"] = variable.getAttribute("instruction") entry["scope"] = variable.getAttribute("scope") addrlist = [] addresstypes = variable.getElementsByTagName("address") for address in addresstypes: addr = address.firstChild.data opr = address.getAttribute("operation") addrtuple = (addr, opr) addrlist.append(addrtuple) entry["address"] = addrlist parsedmeas.variables["variable"].append(entry) except: print("Unexpected error: " + sys.exc_info()[0]) return None return parsedmeas # Print SPHYNX Header printHeader() # Variables MeasLocation = sys.argv[1] MeasSpecs = [] # Parse and Organize ASPs for root, directories, files in os.walk(MeasLocation): for filename in files: if filename.endswith(".xml"): parsedmeas = parseMeasXml(MeasLocation + filename); if (parsedmeas is not None): MeasSpecs.append(parsedmeas) break; # only top folder, break before recursively checking test folders # Print APBs for MeasSpec in MeasSpecs: printMeasInfo(MeasSpec)