22 bool continue_through_exit_ep(
const Endpoint* exit_ep,
const u32 current_depth)
24 if (exit_ep ==
nullptr)
31 if (current_depth != 0)
35 else if (control_types.find(exit_ep->get_pin()->get_type()) != control_types.end())
44 bool continue_through_entry_ep(
const Endpoint* entry_ep,
const u32 current_depth)
46 if (entry_ep ==
nullptr)
51 if (control_types.find(entry_ep->get_pin()->get_type()) != control_types.end())
56 const auto* gt = entry_ep->get_gate()->get_type();
71 bool operator==(
const GraphCandidate& rhs)
const
73 return this->size == rhs.size && this->in_reg == rhs.in_reg && this->out_reg == rhs.out_reg;
76 bool operator<(
const GraphCandidate& rhs)
const
78 return this->size > rhs.size || (this->size == rhs.size && this->in_reg > rhs.in_reg) || (this->size == rhs.size && this->in_reg == rhs.in_reg && this->out_reg > rhs.out_reg);
82 igraph_error_t get_saturating_neighborhoods(
const igraph_t* graph, igraph_vector_int_t* in_set, igraph_vector_int_t* out_set, igraph_integer_t node, igraph_integer_t timeout)
84 igraph_integer_t no_of_nodes = igraph_vcount(graph);
85 igraph_integer_t i, j, k;
87 igraph_vector_int_t current_hood, previous_hood;
88 igraph_vector_int_t* current_hood_p = ¤t_hood;
89 igraph_vector_int_t* previous_hood_p = &previous_hood;
90 igraph_vector_int_t tmp;
94 IGRAPH_ERROR(
"Negative timeout", IGRAPH_EINVAL);
97 added = IGRAPH_CALLOC(no_of_nodes, igraph_bool_t);
98 IGRAPH_CHECK_OOM(added,
"Cannot calculate neighborhood size.");
99 IGRAPH_FINALLY(igraph_free, added);
101 IGRAPH_VECTOR_INT_INIT_FINALLY(current_hood_p, 0);
102 IGRAPH_VECTOR_INT_INIT_FINALLY(previous_hood_p, 0);
103 IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0);
105 IGRAPH_CHECK(igraph_vector_int_init(in_set, 0));
106 IGRAPH_CHECK(igraph_vector_int_init(out_set, 0));
107 igraph_vector_int_clear(in_set);
108 igraph_vector_int_clear(out_set);
110 IGRAPH_CHECK(igraph_vector_int_push_back(current_hood_p, node));
112 igraph_integer_t previous_size, current_size;
114 for (i = 0; i < timeout; i++)
116 previous_size = igraph_vector_int_size(previous_hood_p);
117 current_size = igraph_vector_int_size(current_hood_p);
119 if (previous_size < current_size)
121 igraph_vector_int_swap(previous_hood_p, current_hood_p);
122 igraph_vector_int_clear(current_hood_p);
124 memset(added,
false, no_of_nodes *
sizeof(igraph_bool_t));
126 for (j = 0; j < current_size; j++)
128 igraph_integer_t actnode = VECTOR(*previous_hood_p)[j];
129 igraph_vector_int_clear(&tmp);
131 IGRAPH_CHECK(igraph_neighbors(graph, &tmp, actnode, IGRAPH_OUT, IGRAPH_LOOPS_TWICE,
true));
133 for (k = 0; k < igraph_vector_int_size(&tmp); k++)
135 igraph_integer_t nei = VECTOR(tmp)[k];
139 IGRAPH_CHECK(igraph_vector_int_push_back(current_hood_p, nei));
146 if (previous_size == current_size)
148 IGRAPH_CHECK(igraph_vector_int_update(in_set, previous_hood_p));
149 IGRAPH_CHECK(igraph_vector_int_update(out_set, current_hood_p));
156 igraph_vector_int_destroy(current_hood_p);
157 igraph_vector_int_destroy(previous_hood_p);
158 igraph_vector_int_destroy(&tmp);
160 IGRAPH_FINALLY_CLEAN(4);
162 return IGRAPH_SUCCESS;
165 igraph_error_t get_saturating_neighborhoods_scc(
const igraph_t* graph,
166 igraph_vector_int_t* in_set,
167 igraph_vector_int_t* out_set,
168 igraph_integer_t node,
169 igraph_integer_t timeout,
170 std::map<std::set<u32>, igraph_vector_int_t*>& cache)
172 igraph_integer_t no_of_nodes = igraph_vcount(graph);
173 igraph_integer_t i, j, k;
174 igraph_bool_t* added;
175 igraph_vector_int_t current_hood, previous_hood;
176 igraph_vector_int_t current_component, previous_component;
177 igraph_vector_int_t* current_hood_p = ¤t_hood;
178 igraph_vector_int_t* previous_hood_p = &previous_hood;
179 igraph_vector_int_t* current_component_p = ¤t_component;
180 igraph_vector_int_t* previous_component_p = &previous_component;
181 igraph_vector_int_t tmp;
185 IGRAPH_ERROR(
"Negative timeout", IGRAPH_EINVAL);
188 added = IGRAPH_CALLOC(no_of_nodes, igraph_bool_t);
189 IGRAPH_CHECK_OOM(added,
"Cannot calculate neighborhood size.");
190 IGRAPH_FINALLY(igraph_free, added);
192 IGRAPH_VECTOR_INT_INIT_FINALLY(current_hood_p, 0);
193 IGRAPH_VECTOR_INT_INIT_FINALLY(previous_hood_p, 0);
194 IGRAPH_VECTOR_INT_INIT_FINALLY(current_component_p, 0);
195 IGRAPH_VECTOR_INT_INIT_FINALLY(previous_component_p, 0);
196 IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0);
198 IGRAPH_CHECK(igraph_vector_int_init(in_set, 0));
199 IGRAPH_CHECK(igraph_vector_int_init(out_set, 0));
200 igraph_vector_int_clear(in_set);
201 igraph_vector_int_clear(out_set);
203 IGRAPH_CHECK(igraph_vector_int_push_back(current_hood_p, node));
204 IGRAPH_CHECK(igraph_vector_int_push_back(current_component_p, node));
206 igraph_integer_t previous_size, current_size;
208 for (i = 0; i < timeout; i++)
210 previous_size = igraph_vector_int_size(previous_component_p);
211 current_size = igraph_vector_int_size(current_component_p);
212 u32 current_hood_size = igraph_vector_int_size(current_hood_p);
214 if (previous_size < current_size || current_size == 1)
217 igraph_vector_int_swap(previous_hood_p, current_hood_p);
218 igraph_vector_int_swap(previous_component_p, current_component_p);
219 igraph_vector_int_clear(current_hood_p);
220 igraph_vector_int_clear(current_component_p);
223 memset(added,
false, no_of_nodes *
sizeof(igraph_bool_t));
225 std::set<u32> cache_key;
226 for (j = 0; j < current_hood_size; j++)
228 igraph_integer_t actnode = VECTOR(*previous_hood_p)[j];
229 igraph_vector_int_clear(&tmp);
231 IGRAPH_CHECK(igraph_neighbors(graph, &tmp, actnode, IGRAPH_OUT, IGRAPH_LOOPS_TWICE,
true));
233 for (k = 0; k < igraph_vector_int_size(&tmp); k++)
235 igraph_integer_t nei = VECTOR(tmp)[k];
239 IGRAPH_CHECK(igraph_vector_int_push_back(current_hood_p, nei));
240 cache_key.insert(nei);
250 if (
const auto cache_it = cache.find(cache_key); cache_it != cache.end())
252 IGRAPH_CHECK(igraph_vector_int_update(current_component_p, cache_it->second));
257 igraph_vs_t subgraph_vertices = igraph_vss_vector(current_hood_p);
258 IGRAPH_FINALLY(igraph_vs_destroy, &subgraph_vertices);
259 igraph_vector_int_t vertex_map;
260 IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_map, igraph_vector_int_size(current_hood_p));
261 IGRAPH_CHECK(igraph_induced_subgraph_map(graph, &subgraph, subgraph_vertices, IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH,
nullptr, &vertex_map));
262 IGRAPH_FINALLY(igraph_destroy, &subgraph);
264 igraph_vector_int_t membership, csize;
265 IGRAPH_VECTOR_INT_INIT_FINALLY(&membership, 0);
266 IGRAPH_VECTOR_INT_INIT_FINALLY(&csize, 0);
267 IGRAPH_CHECK(igraph_connected_components(&subgraph, &membership, &csize,
nullptr, IGRAPH_STRONG));
269 u32 max_id = igraph_vector_int_which_max(&csize);
270 u32 num_subgraph_vertices = igraph_vcount(&subgraph);
271 for (
i32 i = 0; i < num_subgraph_vertices; i++)
273 u32 cid = VECTOR(membership)[i];
276 IGRAPH_CHECK(igraph_vector_int_push_back(current_component_p, VECTOR(vertex_map)[i]));
280 igraph_vs_destroy(&subgraph_vertices);
281 igraph_vector_int_destroy(&vertex_map);
282 igraph_vector_int_destroy(&membership);
283 igraph_vector_int_destroy(&csize);
284 igraph_destroy(&subgraph);
285 IGRAPH_FINALLY_CLEAN(5);
287 igraph_vector_int_t* cache_tmp =
new igraph_vector_int_t;
288 IGRAPH_CHECK(igraph_vector_int_init(cache_tmp, 0));
289 IGRAPH_CHECK(igraph_vector_int_update(cache_tmp, current_component_p));
290 cache[cache_key] = cache_tmp;
295 if (previous_size == current_size)
297 IGRAPH_CHECK(igraph_vector_int_update(in_set, previous_component_p));
298 IGRAPH_CHECK(igraph_vector_int_update(out_set, current_component_p));
305 igraph_vector_int_destroy(current_hood_p);
306 igraph_vector_int_destroy(previous_hood_p);
307 igraph_vector_int_destroy(current_component_p);
308 igraph_vector_int_destroy(previous_component_p);
309 igraph_vector_int_destroy(&tmp);
311 IGRAPH_FINALLY_CLEAN(6);
313 return IGRAPH_SUCCESS;
321 return ERR(
"netlist is a nullptr");
324 log_info(
"hawkeye",
"start detecting state register candidates...");
325 auto start = std::chrono::system_clock::now();
330 std::map<Gate*, std::set<Gate*>> ff_map;
331 std::unordered_map<const Net*, std::set<Gate*>> cache = {};
333 for (
auto* sg : start_gates)
335 if (
const auto res = nl_dec.get_next_matching_gates(
336 sg,
true, [](
const Gate* g) { return g->get_type()->has_property(GateTypeProperty::ff); },
false, continue_through_exit_ep, continue_through_entry_ep);
339 ff_map[sg] = res.get();
343 return ERR(res.get_error());
350 return ERR(res.get_error());
352 auto base_graph = res.get();
354 const auto start_vertices_res = base_graph->get_vertices_from_gates(start_ffs.empty() ? start_gates : start_ffs);
355 if (start_vertices_res.is_error())
357 return ERR(start_vertices_res.get_error());
359 auto start_vertices = start_vertices_res.get();
363 const auto sorted_ids = [](
const std::set<Gate*>& gates) {
364 std::vector<u32> res;
365 res.reserve(gates.size());
366 for (
const auto* g : gates)
368 res.push_back(g->
get_id());
370 std::sort(res.begin(), res.end());
374 std::map<std::pair<std::vector<u32>, std::vector<u32>>,
CipherCandidate> unique_candidates;
375 for (
const auto& config : configs)
377 auto tmp_graph_res = base_graph->copy();
378 if (tmp_graph_res.is_error())
380 return ERR(tmp_graph_res.get_error());
382 auto tmp_graph = tmp_graph_res.get();
384 std::map<Gate*, std::set<Gate*>> filtered_map;
387 filtered_map = std::move(ff_map);
388 if (
const auto edge_res = tmp_graph->add_edges(ff_map); edge_res.is_error())
390 return ERR(edge_res.get_error());
395 std::map<const GateType*, std::set<const GateType*>> allowed_gate_type_map;
397 for (
const auto& gt_list : config.equivalent_types)
399 std::set<const GateType*> types;
400 for (
const auto& gt_name : gt_list)
402 types.insert(gl->get_gate_type_by_name(gt_name));
405 for (
const auto* gt : types)
407 allowed_gate_type_map[gt] = types;
411 for (
const auto& [src, dsts] : ff_map)
413 for (
auto* dst : dsts)
415 const auto* src_type = src->get_type();
416 const auto* dst_type = dst->get_type();
417 if (src_type != dst_type)
419 if (
const auto src_it = allowed_gate_type_map.find(src_type); src_it != allowed_gate_type_map.end())
421 const auto& allowed_gates = std::get<1>(*src_it);
422 if (allowed_gates.find(dst_type) == allowed_gates.end())
433 filtered_map[src].insert(dst);
439 std::unordered_map<const Gate*, std::map<PinType, const Net*>> control_map;
440 for (
const auto* gate : start_gates)
442 control_map[gate] = std::map<PinType, const Net*>();
444 for (
const auto& ep : gate->get_fan_in_endpoints())
446 if (
auto pin_type = ep->get_pin()->get_type(); control_types.find(pin_type) != control_types.end())
448 control_map[gate][pin_type] = ep->get_net();
453 for (
const auto& [src, dsts] : ff_map)
455 for (
auto* dst : dsts)
457 if (control_map.at(src) != control_map.at(dst))
462 filtered_map[src].insert(dst);
468 std::unordered_map<const Gate*, std::set<PinType>> control_map;
469 for (
const auto* gate : start_gates)
471 control_map[gate] = std::set<PinType>();
473 for (
const auto& ep : gate->get_fan_in_endpoints())
475 auto sources = ep->get_net()->get_sources();
476 if (sources.size() != 1)
480 if (sources.at(0)->get_gate()->is_gnd_gate() || sources.at(0)->get_gate()->is_vcc_gate())
485 if (
auto pin_type = ep->get_pin()->get_type(); control_types.find(pin_type) != control_types.end())
487 control_map[gate].insert(pin_type);
492 for (
const auto& [src, dsts] : ff_map)
494 for (
auto* dst : dsts)
501 if (control_map.at(src) != control_map.at(dst))
506 filtered_map[src].insert(dst);
511 if (
const auto edge_res = tmp_graph->add_edges(filtered_map); edge_res.is_error())
513 return ERR(edge_res.get_error());
516 igraph_vector_int_t in_set, out_set;
517 if (
const auto res = igraph_vector_int_init(&in_set, 0); res != IGRAPH_SUCCESS)
519 return ERR(igraph_strerror(res));
522 if (
const auto res = igraph_vector_int_init(&out_set, 0); res != IGRAPH_SUCCESS)
524 igraph_vector_int_destroy(&in_set);
525 return ERR(igraph_strerror(res));
528 std::set<GraphCandidate> graph_candidates;
532 for (
const auto v : start_vertices)
534 igraph_vector_int_clear(&in_set);
535 igraph_vector_int_clear(&out_set);
537 if (
const auto res = get_saturating_neighborhoods(tmp_graph->get_graph(), &in_set, &out_set, v, config.timeout); res != IGRAPH_SUCCESS)
539 igraph_vector_int_destroy(&in_set);
540 igraph_vector_int_destroy(&out_set);
541 return ERR(igraph_strerror(res));
544 u32 size = igraph_vector_int_size(&out_set);
545 if (
size <= config.min_register_size)
552 for (
u32 i = 0; i < igraph_vector_int_size(&in_set); i++)
554 c.in_reg.insert(VECTOR(in_set)[i]);
556 for (
u32 i = 0; i < igraph_vector_int_size(&out_set); i++)
558 c.out_reg.insert(VECTOR(out_set)[i]);
560 graph_candidates.insert(c);
565 std::map<std::set<u32>, igraph_vector_int_t*> scc_cache;
567 for (
const auto v : start_vertices)
569 igraph_vector_int_clear(&in_set);
570 igraph_vector_int_clear(&out_set);
572 if (
const auto res = get_saturating_neighborhoods_scc(tmp_graph->get_graph(), &in_set, &out_set, v, config.timeout, scc_cache); res != IGRAPH_SUCCESS)
574 igraph_vector_int_destroy(&in_set);
575 igraph_vector_int_destroy(&out_set);
576 for (
auto& [_, comp] : scc_cache)
578 igraph_vector_int_destroy(comp);
581 return ERR(igraph_strerror(res));
584 u32 size = igraph_vector_int_size(&out_set);
585 if (
size <= config.min_register_size)
592 for (
u32 i = 0; i < igraph_vector_int_size(&in_set); i++)
594 c.in_reg.insert(VECTOR(in_set)[i]);
596 for (
u32 i = 0; i < igraph_vector_int_size(&out_set); i++)
598 c.out_reg.insert(VECTOR(out_set)[i]);
600 graph_candidates.insert(c);
603 for (
auto& [_, comp] : scc_cache)
605 igraph_vector_int_destroy(comp);
610 igraph_vector_int_destroy(&in_set);
611 igraph_vector_int_destroy(&out_set);
613 for (
const auto& gc : graph_candidates)
617 if (
auto out_reg_res = tmp_graph->get_gates_set_from_vertices(gc.out_reg); out_reg_res.is_ok())
623 return ERR(out_reg_res.get_error());
626 if (gc.in_reg == gc.out_reg)
628 auto key = std::make_pair(sorted_ids(
out_reg), sorted_ids(
out_reg));
634 if (
auto in_reg_res = tmp_graph->get_gates_set_from_vertices(gc.in_reg); in_reg_res.is_ok())
636 in_reg = in_reg_res.get();
637 auto key = std::make_pair(sorted_ids(
in_reg), sorted_ids(
out_reg));
642 return ERR(in_reg_res.get_error());
650 std::vector<CipherCandidate> candidates;
651 candidates.reserve(unique_candidates.size());
652 for (
auto& [_, candidate] : unique_candidates)
654 candidates.push_back(std::move(candidate));
660 const auto by_id = [](
const Gate* lhs,
const Gate* rhs) {
return lhs->get_id() < rhs->get_id(); };
661 std::vector<bool> discard(candidates.size(),
false);
662 for (
u32 i = 0; i < candidates.size(); i++)
664 if (candidates.at(i).get_size() < min_state_size)
670 const auto& outer = candidates.at(i).get_output_reg();
671 for (
u32 j = i + 1; j < candidates.size(); j++)
673 const auto& inner = candidates.at(j).get_output_reg();
674 if (std::includes(outer.begin(), outer.end(), inner.begin(), inner.end(), by_id))
683 for (
u32 i = 0; i < candidates.size(); i++)
693 candidates[kept] = std::move(candidates[i]);
697 candidates.resize(kept);
699 auto duration_in_seconds = std::chrono::duration<double>(std::chrono::system_clock::now() - start).count();
700 if (candidates.size() == 1)
702 log_info(
"hawkeye",
"detected {} state register candidate in {} seconds", candidates.size(), duration_in_seconds);
706 log_info(
"hawkeye",
"detected {} state register candidates in {} seconds", candidates.size(), duration_in_seconds);
709 return OK(std::move(candidates));
This file contains the struct for configuring HAWKEYE's candidate search, see CipherCandidate::detect...
This file contains the class that holds all information on a candidate for a symmetric cryptographic ...
GateType * get_type() const
const std::vector< Gate * > & get_gates() const
const GateLibrary * get_gate_library() const
static Result< std::unique_ptr< NetlistGraph > > from_netlist_no_edges(Netlist *nl, const std::vector< Gate * > &gates={})
Create an empty directed graph from a netlist.
A candidate for a symmetric cryptographic implementation within a netlist.
CipherCandidate()=default
static Result< std::vector< CipherCandidate > > detect(Netlist *nl, const std::vector< DetectionConfiguration > &configs, u32 min_state_size=40, const std::vector< Gate * > &start_ffs={})
Attempt to locate candidates for symmetric cryptographic SPN, Feistel, and ARX implementations within...
#define log_info(channel,...)
This file contains functions related to neighborhoods in graphs.
This file contains the class that holds a netlist graph.
@ CHECK_NETS
If two flip-flops ff1 and ff2 are connected through combinational logic and are controlled through th...
@ CHECK_TYPE
If two flip-flops ff1 and ff2 are connected through combinational logic and are of the same gate type...
@ CHECK_FF
If two flip-flops ff1 and ff2 are connected through combinational logic, an edge is added such that (...
@ CHECK_PINS
If two flip-flops ff1 and ff2 are connected through combinational logic and are controlled through th...
@ CHECK_SCC
Use SCC detection within the currently explored neighborhood of a start flip-flop.
@ NONE
Do not use SCC detection and instead resort to the simple neighborhood discovery algorithm.