def rnd_obf(args, ast): ast_mgr = ASTWrapper(ast, args.b) circuit = ast_mgr.get_circuit(req_fanout=True, correct_order=False) wires = circuit.wire_objs i = 0 obf_gates = [] new_wires = [] rep_wire_names = [] rep_instances = [] key_list = "" while i < args.k: rnd_w = wires[randint(0, len(wires) - 1)] if (rnd_w.type != Wire.INPUT) and (rnd_w.fanout != 0) and ( rnd_w.instance not in rep_instances): key_val = randint(0, 1) key_list += str(key_val) if key_val == 0: gate_type = "xor" else: gate_type = "xnor" logging.warning(rnd_w.name + " is selected for obfuscation") new_w_name = rnd_w.name + "_obf" new_wires.append(new_w_name) rep_wire_names.append({rnd_w.name: new_w_name}) rep_instances.append(rnd_w.instance) key_ports = Wire("keyinput" + str(i), "inp", [], 0) obf_gates.append(Wire(rnd_w.name, gate_type, [rnd_w, key_ports], 0)) rnd_w.name = new_w_name i = i + 1 # old wire names should be replaced in their instances (for example G13 to G13_obf) ast_mgr.replace_wire_names(rep_wire_names, rep_instances) for i in range(args.k): ast_mgr.add_ports('keyinput{}'.format(i), 0, 'input') ast_mgr.add_wires(new_wires) ast_mgr.add_instances(obf_gates) ast_mgr.change_module_name(circuit.name + "_obf") bench_address = circuit.path bench_folder = bench_address[0:bench_address. rfind("/", 0, bench_address.rfind("/"))] + "/" output_folder = bench_folder + args.m + "/" obf_file_name = circuit.name + "_" + str(args.k) + ".v" write_verilog(ast_mgr.ast, obf_file_name, output_folder) key_list = key_list[::-1] key_list = "// key=" + key_list with open(output_folder + obf_file_name, 'r+') as f: content = f.read() f.seek(0, 0) f.write(key_list.rstrip('\r\n') + '\n' + content) return
def rnd_obf(args): circuit = verilog2circuit(args.b) circuit.wire_objs = read_verilog_wires(args.b, 'generic') wires = circuit.wire_objs i = 0 obf_gates = {} rep_instances = [] key_list = "" while i < args.k: rnd_w = circuit.get_random_wire() if (rnd_w not in rep_instances) and (wires[rnd_w].type != Wire.DFF) and \ (wires[rnd_w] != Wire.INPUT) and (rnd_w not in circuit.output_wires): logging.warning(rnd_w + " is selected for obfuscation") key_val = randint(0, 1) if key_val == 0: gate_type = "xor" key_list += '0' else: gate_type = "xnor" key_list += '1' rep_instances.append(rnd_w) w_tmp = rnd_w + '_obf' obf_gates[rnd_w] = Wire(rnd_w, gate_type, ['keyinput[{}]'.format(i), w_tmp]) obf_gates[w_tmp] = Wire(w_tmp, wires[rnd_w].type, wires[rnd_w].operands) obf_gates[w_tmp].tag = wires[rnd_w].tag i = i + 1 # old wire names should be replaced in their instances (e.g., G13 to G13_obf) for w in rep_instances: del wires[w] for w in obf_gates: wires[w] = obf_gates[w] circuit.input_wires['keyinput'] = i circuit.port_defs.append('keyinput') bench_address = circuit.folder_path output_folder = bench_address[0: bench_address.rfind("/", 0, bench_address.rfind("/"))] + "/{}/".format(args.m) obf_file_name = circuit.name + "_" + str(args.k) + ".v" verilog_text = circuit2verilog(circuit, circuit.name + "_obf") key_list = key_list[::-1] key_list = "// key=" + key_list with open(output_folder + obf_file_name, 'w') as f: f.write(key_list.rstrip('\r\n') + '\n\n') f.write(verilog_text) return
def create_wire(type, in_port, out_port): w = None if 'nand' in type: assert len(in_port) > 1 w = Wire(out_port, 'nand', in_port) elif 'and' in type: assert len(in_port) > 1 w = Wire(out_port, 'and', in_port) elif 'xnor' in type: assert len(in_port) > 1 w = Wire(out_port, 'xnor', in_port) elif 'xor' in type: assert len(in_port) > 1 w = Wire(out_port, 'xor', in_port) elif 'nor' in type: assert len(in_port) > 1 w = Wire(out_port, 'nor', in_port) elif 'or' in type: assert len(in_port) > 1 w = Wire(out_port, 'or', in_port) elif ('inv' in type) or ('not' in type): assert len(in_port) == 1 w = Wire(out_port, 'not', in_port) elif 'buf' in type: assert len(in_port) == 1 w = Wire(out_port, 'buf', in_port) elif 'mx2' in type: assert len(in_port) > 1 w = Wire(out_port, 'mux', in_port) elif 'mux' in type: assert len(in_port) > 1 w = Wire(out_port, 'mux', in_port) else: logging.critical('undefined gate type: {}'.format(type)) exit() return w
def read_dffwrst_wires(path): # for designs with ffs with rst port try: with open(path, "r") as f: design = f.read() line = ''.join(design) except EnvironmentError: logging.critical('{} not found'.format(path)) exit() wires = {} # match assign assigns = re.findall(r'assign (.*?) = (.*?);', line) for a in assigns: wires[a[0]] = Wire(a[0], 'buf', 1) wires[a[0]].operands = [a[1]] # match gates # g[0]: gate type; g[1]: gate tag; g[2]: gate ports gates = re.findall(r'^\s*(\S*?)\s*(\S*?)\s*\(([\s\S]*?)\);', line, re.MULTILINE) for g in gates: ports = g[2].replace(' ', '').replace('\n', '').split(',') type = g[0].lower() if 'dff' in type: in_port = ports[4] q_port = ports[1] wires[q_port] = Wire(q_port, 'dff', 1) wires[q_port].operands = [in_port] wires[q_port].tag = g[1] else: if 'module' not in type: out_port = ports[0] in_port = ports[1:] wires[out_port] = create_wire(type, in_port, out_port) wires[out_port].operands = in_port wires[out_port].tag = g[1] return wires
def lbll_obf(args): # latch-based obfuscation circuit = verilog2circuit(args.b) circuit.wire_objs = read_verilog_wires(args.b, 'generic') wires = circuit.wire_objs i = 0 obf_gates = {} rep_instances = [] key_list = "" while i <= args.k: r = randint(0, 2) if r == 0: for w in wires: if (w not in rep_instances) and (wires[w].type == Wire.DFF): logging.warning(w + " is selected as modified FF") rep_instances.append(w) w_tmp = w + '_obf' obf_gates[w_tmp] = Wire(w_tmp, 'lat', ['keyinput[{}]'.format(i), 'keyinput[{}]'.format(i+1), wires[w].operands[0]]) obf_gates[w] = Wire(w, 'lat', ['keyinput[{}]'.format(i+2), 'keyinput[{}]'.format(i+3), w_tmp]) key_list += '1001' i = i + 4 break elif r == 1: for w in wires: if (w not in rep_instances) and (wires[w].type != Wire.DFF): logging.warning(w + " is selected as path delay decoy") rep_instances.append(w) w_tmp = w + '_obf' obf_gates[w_tmp] = wires[w] obf_gates[w_tmp].name = w_tmp obf_gates[w] = Wire(w, 'lat', ['keyinput[{}]'.format(i), 'keyinput[{}]'.format(i+1), w_tmp]) key_list += '11' i = i + 2 break else: # continue for w1 in wires: if (w1 not in rep_instances) and (wires[w1].type != Wire.DFF): for w2 in wires: if (w2 not in rep_instances) and (wires[w2].type != Wire.DFF) and (w2 != w1): logging.warning(w1 + " is selected as logic decoy") rep_instances.append(w1) w_tmp = w1 + '_obf' obf_gates[w_tmp] = wires[w1] obf_gates[w_tmp].name = w_tmp lat_tmp = 'w_lat{}'.format(i) obf_gates[lat_tmp] = Wire(lat_tmp, 'lat', ['keyinput[{}]'.format(i), 'keyinput[{}]'.format(i + 1), w2]) obf_gates[w1] = Wire(w1, 'or', [w_tmp, lat_tmp]) key_list += '00' i = i + 2 break break for w in rep_instances: del wires[w] for w in obf_gates: wires[w] = obf_gates[w] circuit.input_wires['keyinput'] = i circuit.port_defs.append('keyinput') bench_address = circuit.folder_path output_folder = bench_address[0: bench_address.rfind("/", 0, bench_address.rfind("/"))] + "/{}/".format(args.m) obf_file_name = circuit.name + "_" + str(args.k) + ".v" verilog_text = circuit2verilog(circuit, circuit.name + "_obf") key_list = key_list[::-1] key_list = "// key=" + key_list with open(output_folder + obf_file_name, 'w') as f: f.write(key_list.rstrip('\r\n') + '\n\n') f.write(verilog_text) return
def bench2circuit(path): try: with open(path, "r") as f: bench_file = f.read() except EnvironmentError: logging.critical('{} not found'.format(path)) exit() inputs = re.findall(r'INPUT\((.*?)\)', bench_file) outputs = re.findall(r'OUTPUT\((.*?)\)', bench_file) gates_match = re.findall(r'(.*?) = (.*?)\((.*?)\)', bench_file) wires = {} for g in gates_match: in_port = g[2].replace(' ', '').replace('\n', '').split(',') out_port = g[0] gate_type = g[1].lower() if 'dff' in gate_type: assert len(in_port) == 1 wires[out_port] = Wire(out_port, 'dff', in_port) elif 'nand' in gate_type: assert len(in_port) > 1 wires[out_port] = Wire(out_port, 'nand', in_port) elif 'and' in gate_type: assert len(in_port) > 1 wires[out_port] = Wire(out_port, 'and', in_port) elif 'xnor' in gate_type: assert len(in_port) > 1 wires[out_port] = Wire(out_port, 'xnor', in_port) elif 'xor' in gate_type: assert len(in_port) > 1 wires[out_port] = Wire(out_port, 'xor', in_port) elif 'nor' in gate_type: assert len(in_port) > 1 wires[out_port] = Wire(out_port, 'nor', in_port) elif 'or' in gate_type: assert len(in_port) > 1 wires[out_port] = Wire(out_port, 'or', in_port) elif 'not' in gate_type: assert len(in_port) == 1 wires[out_port] = Wire(out_port, 'not', in_port) elif 'buf' in gate_type: assert len(in_port) == 1 wires[out_port] = Wire(out_port, 'buf', in_port) elif 'mux' in gate_type: assert len(in_port) == 3 wires[out_port] = Wire(out_port, 'mux', in_port) else: logging.critical('undefined gate type: {}'.format(gate_type)) exit() wires[out_port].operands = in_port for w in wires: for o in wires[w].operands: if (o in wires) or (o in inputs): continue else: logging.critical( 'wire {} is used but is not connected to anywhere'.format( o)) exit() keys = [] for i in inputs: if 'keyinput' in i: keys.append(i) for k in keys: inputs.remove(k) circuit = Circuit(path[path.rfind("/") + 1:path.rfind(".")]) circuit.folder_path = path[:path.rfind("/") + 1] circuit.file_name = path[path.rfind("/") + 1:] circuit.input_wires = inputs circuit.output_wires = outputs circuit.wire_objs = wires circuit.key_wires = keys logging.warning( 'circuit: {}, inputs: {}, outputs: {}, keyinputs: {}'.format( circuit.name, len(circuit.input_wires), len(circuit.output_wires), len(keys))) return circuit
def __init__(self, orcl_cir, obf_cir): self.orcl_cir = orcl_cir self.obf_cir = obf_cir self.key_subs = [{}, {}] self.dip_ckt0 = [] self.dip_ckt1 = [] self.dip_ckt0_frame = [] self.dip_ckt1_frame = [] self.oracle_ckt_frame = [] # generate solver formulas gen_wire_formulas(self.orcl_cir) gen_wire_formulas(self.obf_cir) # create symbols for primary outputs and next state wires orcl_wires = self.orcl_cir.wire_objs obf_wires = self.obf_cir.wire_objs for w in self.orcl_cir.output_wires: orcl_wires[w].formula = Iff(Symbol(w), orcl_wires[w].formula) for w in set(orcl_cir.next_state_wires): if w in orcl_cir.input_wires: # TODO: test for the next states that are connecting to the inputs print(w) orcl_wires[w] = Wire(w, Wire.INPUT, []) orcl_wires[w].formula = Symbol(w) else: orcl_wires[w].formula = Iff(Symbol(w), orcl_wires[w].formula) for w in self.obf_cir.output_wires: obf_wires[w].formula = Iff(Symbol(w), obf_wires[w].formula) for w in set(obf_cir.next_state_wires): if w not in obf_cir.output_wires: if w in obf_cir.input_wires: obf_wires[w] = Wire(w, Wire.INPUT, []) obf_wires[w].formula = Symbol(w) else: obf_wires[w].formula = Iff(Symbol(w), obf_wires[w].formula) # create key substitution dictionary for w in self.obf_cir.key_wires: self.key_subs[0][Symbol(w)] = Symbol(w + '_0') self.key_subs[1][Symbol(w)] = Symbol(w + '_1') # generate formulas for two copies of obfuscated circuit for w in self.obf_cir.output_wires: self.dip_ckt0.append( substitute(obf_wires[w].formula, self.key_subs[0])) self.dip_ckt1.append( substitute(obf_wires[w].formula, self.key_subs[1])) # converted to set to ignore duplicate next_state_wires for w in self.obf_cir.next_state_wires: self.dip_ckt0.append( substitute(obf_wires[w].formula, self.key_subs[0])) self.dip_ckt1.append( substitute(obf_wires[w].formula, self.key_subs[1])) # key inequality circuit key_xors = [] for w in self.obf_cir.key_wires: key_xors.append( Xor(Symbol('{}_0'.format(w)), Symbol('{}_1'.format(w)))) self.key_inequality_ckt = Or(key_xors)
def build_ce(cir): # ce_netlist = copy.copy(obf_cir.raw_netlist) rep_wire_names = {} seq_elements = [] count = 0 # cir = obf_cir cir.wire_objs = read_verilog_wires(cir.folder_path + cir.file_name, 'generic') added_wires = {} for w in cir.wire_objs: if cir.wire_objs[w].type == 'dff': Q = w D = cir.wire_objs[w].operands[0] rep_wire_names[str(Q)] = "state[" + str(count) + "]" if ("'b" in D) or (D in cir.input_wires) or (D[:D.rfind('[')] in cir.input_wires): # for constant 1'b1 and 1'b0 inputs r = "next_state[{}]".format(count) added_wires[r] = Wire(r, 'buf', [str(D)]) else: rep_wire_names[str(D)] = "next_state[{}]".format(count) seq_elements.append(w) count = count + 1 cir.wire_objs = {**cir.wire_objs, **added_wires} for w in cir.wire_objs: if cir.wire_objs[w].type == 'lat': Q = w D = cir.wire_objs[w].operands[0] rst = cir.wire_objs[w].operands[1] rep_wire_names[str(Q)] = "state[" + str(count) + "]" rep_wire_names[str(D)] = "next_state[" + str(count) + "]" rep_wire_names[str(rst)] = "next_state[" + str(count + 1) + "]" seq_elements.append(w) count = count + 2 # remove dffs/lats for w in seq_elements: cir.wire_objs.pop(w) # change dff input/output wires to next_state/state for r in rep_wire_names: if r in cir.wire_objs: cir.wire_objs[rep_wire_names[r]] = cir.wire_objs[r] cir.wire_objs.pop(r) for w in cir.wire_objs: if r in cir.wire_objs[w].operands: cir.wire_objs[w].operands[cir.wire_objs[w].operands.index( r)] = rep_wire_names[r] # add new buf gates for replaced outputs for r in rep_wire_names: if r in cir.output_wires: cir.wire_objs[r] = Wire(r, 'buf', [rep_wire_names[r]]) # for vectorized wires for r in rep_wire_names: if r[:r.rfind('[')] in cir.output_wires: cir.wire_objs[r] = Wire(r, 'buf', [rep_wire_names[r]]) # add ports cir.port_defs = cir.port_defs + ['state', 'next_state'] cir.input_wires['state'] = count cir.output_wires['next_state'] = count verilog_text = circuit2verilog(cir, cir.name + '_ce') return verilog_text, count
def read_cadence_verilog_wires(path): try: with open(path, "r") as f: design = f.read() line = ''.join(design) except EnvironmentError: logging.critical('{} not found'.format(path)) exit() wires = {} # match assign assigns = re.findall(r'assign (.*?) = (.*?);', line) for a in assigns: wires[a[0]] = Wire(a[0], 'buf', 1) wires[a[0]].operands = [a[1]] # match gates # g[0]: gate type; g[1]: gate tag; g[2]: gate ports gates = re.findall(r'^\s*(\S*?)\s*(\S*?)\s*\(([\s\S]*?)\);', line, re.MULTILINE) for g in gates: ports = g[2].replace(' ', '').replace('\n', '') gate_ports = re.findall(r'\.(.*?)\s*\((.*?)\)', ports) type = g[0].lower() if 'dff' in type: in_port = '' q_port = '' qn_port = '' for p in gate_ports: if p[0] == 'D': in_port = p[1] elif p[0] == 'Q': q_port = p[1] elif p[0] == 'QN': qn_port = p[1] if q_port and qn_port: wires[q_port] = Wire(q_port, 'dff', 1) wires[q_port].operands = [in_port] wires[qn_port] = Wire(qn_port, 'not', 1) wires[qn_port].operands = [q_port] elif q_port: wires[q_port] = Wire(q_port, 'dff', 1) wires[q_port].operands = [in_port] elif qn_port: q_tmp = 'tmp_' + qn_port wires[q_tmp] = Wire(q_tmp, 'dff', 1) wires[q_tmp].operands = [in_port] wires[qn_port] = Wire(qn_port, 'not', 1) wires[qn_port].operands = [q_tmp] else: logging.critical('undefined dff definition: {}'.format(g[1])) exit() else: if 'module' not in type: in_port = [] out_port = '' for p in gate_ports: if p[0] == 'Y': out_port = p[1] elif p[0] == 'S0': in_port.insert(0, p[1]) else: in_port.append(p[1]) wires[out_port] = create_wire(type, in_port, out_port) wires[out_port].operands = in_port return wires