def clonesPartitioned(self): """ Return two lists [labels of necessary clones], [labels of optional clones] as defined in "On quantitative analysis of attack–defense trees with repeated labels". """ basics_prop = self.basic_actions('a') basics_opp = self.basic_actions('d') # create basic assignment ba = BasicAssignment() for b in basics_opp: ba[b] = 2 for b in basics_prop: ba[b] = 0 clones = [label for label in basics_prop if self.isclone(label)] necessary = [] optional = [] # do the bottom up for each of the clones for label in clones: ba[label] = 1 if minSkillLvl.evaluateBU(self, ba) == 1: necessary.append(label) else: optional.append(label) ba[label] = 0 return necessary, optional
def f(): #global root from tkinter import filedialog file = filedialog.askopenfilename() if file == '': return if file[-4:] == '.xml': # ba should load successfully ba = BasicAssignment(path=file) else: # failed to load a tree from tkinter import messagebox messagebox.showinfo( 'Error!', 'Failed to load a basic assignment from specified file. Be sure to select an .xml file produced by ADTool.' ) return # display loaded values in appropriate Entry widgets for i in range(self.ATTACKERS_ACTIONS_NUMBER): # 1. fetch the value val = ba[self.ATTACKERS_BAS[i]] # 2. update&display in appropriate Entry widget, the one in # (i+2, index) grid cell entry = get_widget_from_grid(self.root, i + 2, index) # clear the widget entry.delete(0, len(entry.get())) # modify value entry.insert(0, str(val))
def find_pareto_selected(self): # reset the basic assignment #global BASIC_ASSIGNMENT self.BASIC_ASSIGNMENT = BasicAssignment() self.deactivate_optimal_attacks() self.activate_pareto() self.deactivate_countermeasures() self.lab32.config(text="Selected task requires") if self.PARETO_COSTS.get() == 1: x = " assignment of cost and" else: x = " assignments of cost and" self.lab33.config(text=self.PARETO_COSTS.get() + x) if self.PARETO_SKILLS.get() == 1: x = " assignment of skill/diff." else: x = " assignments of skill/diff." self.lab34.config(text=self.PARETO_SKILLS.get() + x) if self.PARETO_PROBS.get() == 1: x = " assignment of prob." else: x = " assignments of prob." self.lab35.config(text=self.PARETO_PROBS.get() + x) self.lab36.config( text= "for the attacker, and selection of actions executed by the defender." )
def evaluateRBU(self, T, ba, neutralANDp, absorbingANDp): """ Compute the value of the attribute modeled with the 'self' domain in the tree 'T', under the basic assignment 'ba', using the repeated bottom-up evaluation. Suitable for non-increasing attribute domains and trees containing repeated basic actions. The neutral and the absorbing element for the domain's operation 'andp' need to be provided. """ if self.type != 1: print( 'The repeated bottom-up evaluation can not be applied for this domain.' ) return for label in T.basic_actions(): if label not in ba: print('Cannot perform the attribute evaluation: the action "' + label + '" has no value assigned.') # if not T.contains_clones(): # return self.evaluateBU(T, ba) proponent = T.root.type result = None necClones, optClones = T.clonesPartitioned() temp_ba = BasicAssignment() # copy the old values for label in ba: temp_ba[label] = ba[label] # modify the values for necessary clones for label in necClones: temp_ba[label] = neutralANDp # iterate over subsets of optional clones for C in powerset(optClones): optminusC = [] for label in optClones: if label in C: temp_ba[label] = absorbingANDp else: temp_ba[label] = neutralANDp optminusC.append(label) # do the bottom-up itresult = self.evaluateBU(T, temp_ba, proponent) # modify for label in optminusC: itresult = self.andp(itresult, ba[label]) # modify if result == None: result = itresult else: result = self.orp(result, itresult) # take the orginal values of the necessary clones into account for label in necClones: result = self.andp(result, ba[label]) return result
def find_optimal_attacks_selected(self): # reset the basic assignment #global BASIC_ASSIGNMENT self.BASIC_ASSIGNMENT = BasicAssignment() self.activate_optimal_attacks() self.deactivate_pareto() self.deactivate_countermeasures() self.lab32.config(text="Selected task requires") self.lab33.config(text="1 assignment of " + self.ATTRIBUTE.get()) self.lab34.config(text="") self.lab35.config(text="") self.lab36.config( text= "for the attacker, and selection of actions executed by the defender." )
def set_semantics(self): """ Return the set semantics of an attack(-defense) tree T, as formulated in "On quantitative analysis of attack–defense trees with repeated labels" by Kordy et al. Returns a list of two-element lists [[set, set], [set, set], ..., [set, set]], with each element of each of the lists being a set of basic actions. """ ba = BasicAssignment() for label in self.basic_actions('a'): ba[label] = [[set([label]), set()]] for label in self.basic_actions('d'): ba[label] = [[set(), set([label])]] return setSem.evaluateBU(self, ba)
def find_countermeasures_selected(self): # reset the basic assignment #global BASIC_ASSIGNMENT self.BASIC_ASSIGNMENT = BasicAssignment() self.deactivate_optimal_attacks() self.deactivate_pareto() self.activate_countermeasures() self.lab32.config(text="Selected task requires") if self.OPT_PROBLEM.get() == 'coverage': x = "0 assignments of cost" else: x = "1 assignment cost" self.lab33.config(text="1 assignment of cost") self.lab34.config(text="for the defender") self.lab35.config(text="") self.lab36.config(text="and " + x + " for the attacker.")
def import_assignment(self): #global root from tkinter import filedialog file = filedialog.askopenfilename() if file == '': # 'cancel' pressed return if file[-4:] == '.txt': # ba should load successfully ba = BasicAssignment(path=file) else: # failed to load an assignment from tkinter import messagebox messagebox.showinfo( 'Error!', 'Failed to load a basic assignment from specified file. Be sure to select a .txt file produced by OSEAD.' ) return # display loaded values in appropriate Entry widgets # but not if the problem selected is the coverage problem current_task = self.TASK_SELECTED.get() if current_task in [1, 2] or self.OPT_PROBLEM != 'coverage': if current_task == 2: columns_to_fill_in = int(self.PARETO_COSTS.get()) + int( self.PARETO_PROBS.get()) + int(self.PARETO_SKILLS.get()) else: columns_to_fill_in = 1 for i in range(self.ATTACKERS_ACTIONS_NUMBER): for j in range(columns_to_fill_in): # 1. fetch the value if current_task == 2: # try: :) val = ba[self.ATTACKERS_BAS[i].replace('\n', ' ')][0][j] else: val = ba[self.ATTACKERS_BAS[i].replace('\n', ' ')] # 2. update&display in appropriate Entry widget, the one in # (i+2, index) grid cell entry = get_widget_from_grid(self.root, i + 2, j + 1) # clear the widget entry.delete(0, len(entry.get())) # modify value entry.insert(0, str(val))
def root_always_achievable(self): ba = BasicAssignment() for b in self.basic_actions(): ba[b] = 1 return satisfiability.evaluateBU(self, ba) == 1
def defense_semantics(self): """ Return the defense semantics of an attack-defense tree T, as defined in "How well can I secure my system?". Returns a list of two-element lists [[set, set], [set, set], ..., [set, set]], with each element of each of the lists being a set of basic actions. """ attackers_actions = self.basic_actions('a') defenders_actions = self.basic_actions('d') # step 1: create attack strategies if self.contains_clones(): # variant 1: for trees containing repeated basic actions # substep 1: create witnesses ba = BasicAssignment() for b in attackers_actions: ba[b] = [] for b in defenders_actions: ba[b] = [[b]] witnesses = suffWit.evaluateBU(self, ba) # don't forget the empty defense strategy witnesses.append([]) # substep 2: iterate over witnesses, get attack strategies # countering them AS = [] for witness in witnesses: ba = BasicAssignment() for b in attackers_actions: ba[b] = [[b]] for b in defenders_actions: if b in witness: ba[b] = [] else: ba[b] = [[]] candidates = countStrats.evaluateBU(self, ba) # select the minimal ones for AS_countering_witness in minimal_lists(candidates): if AS_countering_witness not in AS: AS.append(AS_countering_witness) else: # variant 2: for trees containing no repeated basic actions (iFM) ba = BasicAssignment() for b in attackers_actions: ba[b] = [[b]] for b in defenders_actions: ba[b] = [[]] AS = attStrat.evaluateBU(self, ba) # At this point AS is the set of all attack strategies in the tree. # step 2: defense strategies countering attack strategies result = [] # 2.1 swap actors proponent = 'd' # 2.2 create a basic assignment for b in self.basic_actions('d'): ba[b] = [[b]] # iterate for A in AS: # modify the basic assignment for b in attackers_actions: if b in A: ba[b] = [] else: ba[b] = [[]] # do the bottom-up candidates = countStrats.evaluateBU(self, ba, proponent) # select the minimal ones for candidate in minimal_lists(candidates): result.append([set(A), set(candidate)]) return result
def __init__(self): self.window = Tk() # the main window :( self.root = Toplevel(self.window) # self.root = Tk() # the basic assignment window :( self.root.withdraw() self.col_width = 30 self.TREE = ADTree() self.BASIC_ASSIGNMENT = BasicAssignment() self.TREE_LOADED = 0 self.FILE_SELECTED = StringVar() self.FILE_SELECTED.set('no file selected') self.TREE_LOADED_MSG = "Path to currently loaded tree:" self.TASK_SELECTED = IntVar() self.TASK_SELECTED.set(0) self.ASSIGNMENTS_WINDOW_OPEN = False self.ASSIGNMENTS_WINDOW_OPENED_BEFORE = False self.ATTACKERS_BAS = [] self.DEFENDERS_BAS = [] self.ATTACKERS_ACTIONS_NUMBER = 0 self.DEFENDERS_ACTIONS_NUMBER = 0 self.DOMAIN = None self.DEF_CHECKBOXES_VARIABLES = [] self.LAST_SELECTED_TASK = None self.PATH = StringVar() self.SET_SEM = [] self.DEF_SEM = [] self.SELECTED_DEFENCES = [] self.RESULT = "" # optimal attacks variables self.NUMBER_ATTACKS = StringVar() self.NUMBER_ATTACKS.set("1") self.ATTRIBUTE = StringVar() self.ATTRIBUTE.set("cost") # pareto optimization variables self.PARETO_COSTS = StringVar() self.PARETO_COSTS.set("0") self.PARETO_SKILLS = StringVar() self.PARETO_SKILLS.set("0") self.PARETO_PROBS = StringVar() self.PARETO_PROBS.set("0") # ILP variables self.BUDGET = StringVar() self.BUDGET.set("0") self.OPT_PROBLEM = StringVar() self.OPT_PROBLEM.set("coverage") # assignment button self.ASSIGNMENT_ON = False self.assignment_button = None # oops self.window.title( "OSEAD: Optimal Strategies Extractor for Attack-Defense trees") self.window.protocol("WM_DELETE_WINDOW", self.on_closing_main_window) #window.resizable(0, 0) # width x height + x_offset + y_offset: width = 985 height = 500 self.window.geometry('{}x{}+20+20'.format(width, height)) self.window.deiconify() # col_count, row_count = window.grid_size() # # for row in range(row_count): # window.grid_rowconfigure(row, minsize=40) self.nbcols = 4 self.col_width = 30 # # general stuff self.bg_colour = "#C5E3BF" # "#A6D785" self.res_button = None self.radio_optimal = Radiobutton( self.window, text="Find optimal attacks", font=("helvetica", 10), relief=GROOVE, state=DISABLED, width=self.col_width - 4, variable=self.TASK_SELECTED, command=self.find_optimal_attacks_selected, value=1, padx=0, anchor='w') self.radio_optimal.grid(column=2, row=1, columnspan=2) self.radio_pareto = Radiobutton(self.window, text="Find Pareto optimal attacks", font=("helvetica", 10), relief=GROOVE, width=self.col_width - 4, state=DISABLED, command=self.find_pareto_selected, variable=self.TASK_SELECTED, value=2, padx=0, anchor='w') self.radio_pareto.grid(column=2, row=6, columnspan=2) self.radio_countermeasures = Radiobutton( self.window, text="Find optimal set of countermeasures", font=("helvetica", 10), relief=GROOVE, width=self.col_width - 4, state=DISABLED, command=self.find_countermeasures_selected, variable=self.TASK_SELECTED, value=3, padx=0, anchor='w') self.radio_countermeasures.grid(column=2, row=11, columnspan=2) # optimal attacks spinboxes self.spin_attacks_number = Spinbox(self.window, from_=1, to=20, width=10, wrap=True, textvariable=self.NUMBER_ATTACKS, state=DISABLED) self.spin_attacks_number.grid(column=3, row=2) self.spin_attribute = Spinbox(self.window, values=('cost', 'skill', 'difficulty', 'probability'), width=10, wrap=True, textvariable=self.ATTRIBUTE, command=self.spinbox_attribute, state=DISABLED) self.spin_attribute.grid(column=3, row=3) # pareto spinboxes self.spin_pareto_costs = Spinbox(self.window, from_=0, to=20, width=10, wrap=True, command=self.spinbox_pareto_costs, textvariable=self.PARETO_COSTS, state=DISABLED) self.spin_pareto_costs.grid(column=3, row=7) self.spin_pareto_skills = Spinbox(self.window, from_=0, to=20, width=10, wrap=True, command=self.spinbox_pareto_skills, textvariable=self.PARETO_SKILLS, state=DISABLED) self.spin_pareto_skills.grid(column=3, row=8) self.spin_pareto_probs = Spinbox(self.window, from_=0, to=20, width=10, wrap=True, command=self.spinbox_pareto_probs, textvariable=self.PARETO_PROBS, state=DISABLED) self.spin_pareto_probs.grid(column=3, row=9) # countermeasures spinboxes self.entry_budget = Entry(self.window, width=10, state=DISABLED, exportselection=0, textvariable=self.BUDGET) self.entry_budget.grid(column=3, row=12) self.spin_counter = Spinbox(self.window, values=("coverage", "attacker's investment"), width=10, wrap=True, textvariable=self.OPT_PROBLEM, command=self.spinbox_ilp, state=DISABLED) self.spin_counter.grid(column=3, row=13) # required assignments self.lab32 = Label(self.window, text="No task selected.", font=("helvetica", 10), relief=FLAT, width=(2 * self.col_width) // 3, anchor='w', justify=LEFT) self.lab32.grid(column=4, row=1) self.lab33 = Label(self.window, text="", font=("helvetica", 10), relief=FLAT, width=(2 * self.col_width) // 3, anchor='w') self.lab33.grid(column=4, row=3) self.lab34 = Label(self.window, text="", font=("helvetica", 10), relief=FLAT, width=(2 * self.col_width) // 3, anchor='w') self.lab34.grid(column=4, row=4) self.lab35 = Label(self.window, text="", font=("helvetica", 10), relief=FLAT, width=(2 * self.col_width) // 3, anchor='w') self.lab35.grid(column=4, row=5) self.lab36 = Label(self.window, text="", font=("helvetica", 10), relief=FLAT, width=self.col_width, anchor='w', justify=LEFT, wraplength=7 * 32) self.lab36.grid(column=4, row=6) self.run_button = Button(self.window, text="Run", state=DISABLED, command=self.run_button_clicked, width=10) self.run_button.grid(column=6, row=3, columnspan=2) self.lab45 = Label(self.window, text="", font=("helvetica", 10), relief=FLAT, width=self.col_width, anchor='w') # , justify=LEFT) self.lab45.grid(column=6, row=5, columnspan=2) self.lab46 = Label(self.window, text="", font=("helvetica", 10), relief=FLAT, width=self.col_width, anchor='w') # , justify=LEFT) self.lab46.grid(column=6, row=6, columnspan=2) self.lab47 = Label(self.window, text="", font=("helvetica", 10), relief=FLAT, width=self.col_width, anchor='w') # , justify=LEFT) self.lab47.grid(column=6, row=7, columnspan=2) self.lab48 = Label(self.window, text="", font=("helvetica", 10), relief=FLAT, width=self.col_width, anchor='w') # , justify=LEFT) self.lab48.grid(column=6, row=8, columnspan=2) self.lab49 = Label(self.window, text="", font=("helvetica", 10), relief=FLAT, width=self.col_width, anchor='w') # , justify=LEFT) self.lab49.grid(column=6, row=9, columnspan=2) self.lab410 = Label(self.window, text="", font=("helvetica", 10), relief=FLAT, width=self.col_width, anchor='w') # , justify=LEFT) self.lab410.grid(column=6, row=11, columnspan=2) # lab37 = Label(window, text="", # font=("helvetica", 10), relief=FLAT, width=(2 * col_width) // 3, anchor='w', justify=LEFT, wraplength=0) # lab37.grid(column=4, row=7, columnspan=2) self.lab1 = Label(self.window, text="TREE SELECTION", font=("verdana", 10), relief=RIDGE, width=self.col_width, bg=self.bg_colour) self.lab1.grid(column=0, row=0, columnspan=2) self.lab12 = Label(self.window, text="Select ADTool .xml output file:", font=("helvetica", 10), relief=FLAT, width=self.col_width, anchor='w') self.lab12.grid(column=0, row=2, columnspan=2) self.browse_button = Button(self.window, text="Browse", command=self.browse_clicked, anchor='e') self.browse_button.grid(column=0, row=3) self.lab13 = Label(self.window, text=self.TREE_LOADED_MSG, font=("helvetica", 10), relief=FLAT, width=self.col_width, anchor='w') self.lab13.grid(column=0, row=5, columnspan=2) self.lab14 = Label(self.window, textvariable=self.FILE_SELECTED, font=("helvetica", 10), relief=FLAT, anchor='w') self.lab14.grid(column=0, row=6) # 2.2 column 2 of 4 self.lab2 = Label(self.window, text="TASK SELECTION", font=("verdana", 10), relief=RIDGE, width=self.col_width, bg=self.bg_colour) # "helvetica") self.lab2.grid(column=2, row=0, columnspan=2) # 2.2.1 find optimal attacks self.lab23 = Label(self.window, text="Number of attacks:", font=("helvetica", 10), relief=FLAT, width=(2 * self.col_width) // 3, anchor='w') self.lab23.grid(column=2, row=2) self.lab24 = Label(self.window, text="Attribute to be optimized:", font=("helvetica", 10), relief=FLAT, width=(2 * self.col_width) // 3, anchor='w') self.lab24.grid(column=2, row=3) # , columnspan=2) # 2.2.2 find pareto optimal attacks self.lab26 = Label(self.window, text="Number of costs:", font=("helvetica", 10), relief=FLAT, width=(2 * self.col_width) // 3, anchor='w') self.lab26.grid(column=2, row=7) self.lab26 = Label(self.window, text="Number of skills/difficulties:", font=("helvetica", 10), relief=FLAT, width=(2 * self.col_width) // 3, anchor='w') self.lab26.grid(column=2, row=8) self.lab27 = Label(self.window, text="Number of probabilities:", font=("helvetica", 10), relief=FLAT, width=(2 * self.col_width) // 3, anchor='w') self.lab27.grid(column=2, row=9) # 2.2.3 find optimal set of countermeasures self.lab28 = Label(self.window, text="Defender's budget:", font=("helvetica", 10), relief=FLAT, width=(2 * self.col_width) // 3, anchor='w') self.lab28.grid(column=2, row=12) self.lab29 = Label(self.window, text="Optimization problem:", font=("helvetica", 10), relief=FLAT, width=(2 * self.col_width) // 3, anchor='w') self.lab29.grid(column=2, row=13) # 2.3 column 3 of 4 self.lab3 = Label(self.window, text="BASIC ASSIGNMENT", font=("verdana", 10), relief=RIDGE, width=self.col_width, bg=self.bg_colour) # "helvetica") self.lab3.grid(column=4, row=0, columnspan=2) # 2.4 column 4 of 4 self.lab4 = Label(self.window, text="RUN ANALYSIS", font=("verdana", 10), relief=RIDGE, width=self.col_width, bg=self.bg_colour) # "helvetica") self.lab4.grid(column=6, row=0, columnspan=2)