18 #include <unordered_map>
19 #include <unordered_set>
32 std::vector<Gate*> sorted_by_id(
const T& gates)
34 std::vector<Gate*> res(gates.begin(), gates.end());
35 std::sort(res.begin(), res.end(), [](
const Gate* lhs,
const Gate* rhs) { return lhs->get_id() < rhs->get_id(); });
39 std::vector<u32> ids_of(
const std::vector<Gate*>& gates)
42 res.reserve(gates.size());
43 for (
const auto* g : gates)
45 res.push_back(g->get_id());
51 constexpr
u32 MAX_SBOX_BITS = 8;
58 constexpr
u32 MAX_CONTROL_BITS = 8;
65 "an S-box tabulated over its state and control inputs must stay within the truth table limit");
68 constexpr
u32 MAX_SBOX_ROWS = 1 << MAX_SBOX_BITS;
79 Result<std::string> lookup_sbox(
const std::vector<std::vector<BooleanFunction::Value>>& rows,
const u32 num_inputs,
const SBoxDatabase& db)
81 if (num_inputs == 0 || num_inputs > MAX_SBOX_BITS)
83 return ERR(
"an S-box of " + std::to_string(num_inputs) +
" input bits is not supported, expected between 1 and " + std::to_string(MAX_SBOX_BITS));
86 const u32 num_rows = 1 << num_inputs;
87 if (rows.size() != num_rows)
89 return ERR(
"expected " + std::to_string(num_rows) +
" rows for " + std::to_string(num_inputs) +
" input bits, got " + std::to_string(rows.size()));
91 const u32 num_outputs = rows.front().size();
93 std::vector<u64> values;
94 values.reserve(num_rows);
95 for (
const auto& row : rows)
98 if (u64_res.is_error())
100 return ERR(u64_res.get_error());
102 values.push_back(u64_res.get());
108 if (num_outputs != num_inputs)
110 std::vector<std::bitset<MAX_SBOX_ROWS>> mat(num_outputs);
111 for (
u32 out = 0; out < num_outputs; out++)
113 for (
u32 row = 0; row < num_rows; row++)
115 mat[out][row] = (values[row] >> out) & 1;
119 for (
u32 row = 0; row < num_rows; row++)
122 for (
u32 out = 0; out < num_outputs; out++)
130 for (
u32 out = pivot + 1; out < num_outputs; out++)
134 mat[out] ^= mat[pivot];
139 std::vector<u32> independent;
140 for (
u32 out = 0; out < num_outputs; out++)
144 independent.push_back(out);
148 if (independent.size() != num_inputs)
150 return OK(std::string());
153 for (
auto& value : values)
156 for (
u32 i = 0; i < independent.size(); i++)
158 reduced |= ((value >> independent.at(i)) & 1) << i;
164 std::vector<u8> sbox;
165 sbox.reserve(values.size());
166 for (
const auto value : values)
168 sbox.push_back((
u8)value);
171 if (std::set<u8>(sbox.begin(), sbox.end()).
size() != sbox.size())
174 return OK(std::string());
177 if (
const auto lookup_res = db.lookup(sbox); lookup_res.is_ok())
179 return OK(lookup_res.get());
181 return OK(std::string());
192 std::unordered_set<Gate*> visited;
194 while (!stack.empty())
196 auto* current_gate = stack.back();
199 if (!visited.insert(current_gate).second)
204 for (
auto* pred_gate : current_gate->get_unique_predecessors())
206 if (in_component.count(pred_gate) && !pred_gate->get_type()->has_property(
GateTypeProperty::ff))
208 stack.push_back(pred_gate);
213 return sorted_by_id(visited);
218 m_in_reg = sorted_by_id(round_reg);
219 m_out_reg = m_in_reg;
220 m_in_reg_ids = ids_of(m_in_reg);
221 m_out_reg_ids = m_in_reg_ids;
222 m_size = m_in_reg.size();
223 m_netlist = m_in_reg.front()->get_netlist();
224 m_is_round_based =
true;
229 m_in_reg = sorted_by_id(
in_reg);
230 m_out_reg = sorted_by_id(
out_reg);
231 m_in_reg_ids = ids_of(m_in_reg);
232 m_out_reg_ids = ids_of(m_out_reg);
233 m_size = m_out_reg.size();
234 m_netlist = m_out_reg.front()->get_netlist();
235 m_is_round_based = m_in_reg == m_out_reg;
243 if (this->m_size != rhs.m_size)
245 return this->m_size > rhs.m_size;
247 if (this->m_in_reg_ids != rhs.m_in_reg_ids)
249 return this->m_in_reg_ids < rhs.m_in_reg_ids;
251 return this->m_out_reg_ids < rhs.m_out_reg_ids;
256 return this->m_size == rhs.m_size && this->m_in_reg_ids == rhs.m_in_reg_ids && this->m_out_reg_ids == rhs.m_out_reg_ids;
271 return m_is_round_based;
276 return m_has_round_function;
291 return m_round_logic;
296 std::set<Gate*> all(m_in_reg.begin(), m_in_reg.end());
297 all.insert(m_out_reg.begin(), m_out_reg.end());
298 all.insert(m_round_logic.begin(), m_round_logic.end());
299 return sorted_by_id(all);
304 std::vector<SBox*> res;
305 res.reserve(m_sboxes.size());
306 for (
const auto& sbox : m_sboxes)
308 res.push_back(sbox.get());
315 return m_graph.get();
320 return m_state_inputs;
325 return m_control_inputs;
330 return m_other_inputs;
335 return m_state_outputs;
340 compute_gate_dependencies();
341 return m_input_ffs_of_gate;
346 compute_gate_dependencies();
347 return m_longest_distance_to_gate;
357 if (!m_has_round_function)
359 return ERR(
"round function has not been computed, call build_round_function first");
364 if (!m_sboxes.empty())
371 const std::unordered_set<Gate*> in_reg_lookup(m_in_reg.begin(), m_in_reg.end());
372 const std::unordered_set<Gate*> out_reg_lookup(m_out_reg.begin(), m_out_reg.end());
377 const auto candidate_gates =
get_gates();
378 const std::unordered_set<Gate*> candidate_lookup(candidate_gates.begin(), candidate_gates.end());
381 if (comp_res.is_error())
383 return ERR(comp_res.get_error());
386 for (
const auto& component_vertices : comp_res.get())
393 std::set<Gate*> component_input_ffs;
394 std::set<Gate*> component_gates;
395 for (
const u32 vertex : component_vertices)
397 const auto gate_res = m_graph->get_gate_from_vertex(vertex);
398 if (gate_res.is_error())
400 return ERR(gate_res.get_error());
402 auto* g = gate_res.get();
408 component_gates.insert(g);
409 if (!m_graph->is_shadow_vertex(vertex) && in_reg_lookup.count(g))
411 component_input_ffs.insert(g);
415 const std::vector<Gate*> component = sorted_by_id(component_gates);
416 const u32 number_input_ffs = component_input_ffs.size();
418 if (number_input_ffs < 3)
424 if (number_input_ffs <= MAX_SBOX_BITS)
427 std::set<Gate*> sbox_output_gates;
428 for (
auto* cand_gate : component)
437 const auto suc_gates = cand_gate->get_unique_successors();
438 if (std::none_of(suc_gates.begin(), suc_gates.end(), [&](
Gate* g) { return candidate_lookup.count(g) && !out_reg_lookup.count(g); }))
440 sbox_output_gates.insert(cand_gate);
444 if (sbox_output_gates.size() == number_input_ffs)
446 auto sbox = std::make_unique<SBox>();
447 sbox->component = component;
448 sbox->input_gates = sorted_by_id(component_input_ffs);
449 sbox->output_gates = sorted_by_id(sbox_output_gates);
450 m_sboxes.push_back(std::move(sbox));
461 compute_gate_dependencies();
463 std::set<Gate*> current_subset = component_input_ffs;
464 std::vector<std::vector<std::set<Gate*>>> input_groupings;
466 const u32 max_distance = m_longest_distance_to_gate.empty() ? 0 : m_longest_distance_to_gate.rbegin()->first;
467 for (
u32 step = 1; step <= max_distance; step++)
469 const auto dist_it = m_longest_distance_to_gate.find(step);
470 if (dist_it == m_longest_distance_to_gate.end())
475 current_subset.insert(dist_it->second.begin(), dist_it->second.end());
478 if (subgraph_res.is_error())
480 return ERR(subgraph_res.get_error());
482 const auto subgraph = std::move(subgraph_res.get());
485 if (sub_comp_res.is_error())
487 return ERR(sub_comp_res.get_error());
491 std::set<u32> group_sizes;
492 std::vector<std::set<Gate*>> input_groups;
493 for (
const auto& sub_component : sub_comp_res.get())
495 auto gates_res = subgraph->get_gates_from_vertices(sub_component);
496 if (gates_res.is_error())
498 return ERR(gates_res.get_error());
504 std::set<Gate*> input_group;
505 for (
auto* sub_gate : gates_res.get())
507 if (component_input_ffs.count(sub_gate))
509 input_group.insert(sub_gate);
513 if (input_group.empty())
517 group_sizes.insert(input_group.size());
518 input_groups.push_back(std::move(input_group));
523 if (group_sizes.size() == 1 && input_groups.size() > 1 && input_groups.front().size() > 1)
525 input_groupings.push_back(std::move(input_groups));
529 for (
const auto& input_groups : input_groupings)
531 for (
const auto& input_group : input_groups)
535 std::set<Gate*> output_group;
536 for (
auto* comp_gate : component)
538 if (in_reg_lookup.count(comp_gate) || out_reg_lookup.count(comp_gate))
543 const auto ffs_it = m_input_ffs_of_gate.find(comp_gate);
544 if (ffs_it == m_input_ffs_of_gate.end() || ffs_it->second.size() <= 1)
551 if (!std::includes(input_group.begin(), input_group.end(), ffs_it->second.begin(), ffs_it->second.end()))
558 bool feeds_other_inputs =
false;
559 for (
auto* suc_gate : comp_gate->get_unique_successors())
561 const auto suc_it = m_input_ffs_of_gate.find(suc_gate);
562 if (!candidate_lookup.count(suc_gate) || suc_it == m_input_ffs_of_gate.end())
566 if (!std::includes(input_group.begin(), input_group.end(), suc_it->second.begin(), suc_it->second.end()))
568 feeds_other_inputs =
true;
572 if (!feeds_other_inputs)
581 auto preds = comp_gate->get_unique_predecessors();
582 preds.erase(std::remove_if(preds.begin(), preds.end(), [&](
Gate* g) { return !candidate_lookup.count(g); }), preds.end());
583 std::sort(preds.begin(), preds.end());
584 if (std::includes(output_group.begin(), output_group.end(), preds.begin(), preds.end()))
590 output_group.insert(comp_gate);
594 std::vector<Gate*> to_delete;
595 for (
auto* out_gate : output_group)
597 auto pred_gates = out_gate->get_unique_predecessors();
598 pred_gates.erase(std::remove_if(pred_gates.begin(), pred_gates.end(), [&](
Gate* g) { return !candidate_lookup.count(g); }), pred_gates.end());
599 if (std::all_of(pred_gates.begin(), pred_gates.end(), [&output_group](
Gate* g) { return output_group.count(g); }))
601 to_delete.push_back(out_gate);
604 for (
auto* del_gate : to_delete)
606 output_group.erase(del_gate);
609 if (input_group.size() > MAX_SBOX_BITS || output_group.empty() || output_group.size() > 20)
614 const std::vector<Gate*> inputs = sorted_by_id(input_group);
615 const std::vector<Gate*> outputs = sorted_by_id(output_group);
617 auto add_sbox = [&](std::vector<Gate*> output_gates) {
618 auto sbox = std::make_unique<SBox>();
619 sbox->component = component;
620 sbox->input_gates = inputs;
621 sbox->output_gates = std::move(output_gates);
622 m_sboxes.push_back(std::move(sbox));
628 if (outputs.size() == inputs.size() + 1)
630 for (
u32 drop = 0; drop < outputs.size(); drop++)
632 std::vector<Gate*> reduced = outputs;
633 reduced.erase(reduced.begin() + drop);
634 add_sbox(std::move(reduced));
637 else if (outputs.size() == inputs.size() + 2)
639 for (
u32 drop_1 = 0; drop_1 < outputs.size(); drop_1++)
641 for (
u32 drop_2 = drop_1 + 1; drop_2 < outputs.size(); drop_2++)
643 std::vector<Gate*> reduced = outputs;
644 reduced.erase(reduced.begin() + drop_2);
645 reduced.erase(reduced.begin() + drop_1);
646 add_sbox(std::move(reduced));
658 log_info(
"hawkeye",
"located {} S-boxes within the round function of the candidate.", m_sboxes.size());
664 if (m_netlist ==
nullptr)
666 return ERR(
"candidate is empty");
673 m_round_logic.clear();
674 m_state_inputs.clear();
675 m_state_outputs.clear();
676 m_control_inputs.clear();
677 m_other_inputs.clear();
678 m_input_ffs_of_gate.clear();
679 m_longest_distance_to_gate.clear();
680 m_has_gate_dependencies =
false;
682 m_has_round_function =
false;
684 const std::unordered_set<Gate*> in_reg_lookup(m_in_reg.begin(), m_in_reg.end());
685 std::set<Gate*> state_logic;
690 for (
const auto* out_ff : m_out_reg)
693 if (ff_data_predecessors.size() != 1)
699 const auto* pred_ep = ff_data_predecessors.at(0);
700 auto* first_comb_gate = pred_ep->get_gate();
705 m_state_outputs.insert(pred_ep->get_net());
707 std::unordered_set<Gate*> visited;
708 std::vector<Gate*> stack = {first_comb_gate};
709 std::vector<Gate*> previous;
710 while (!stack.empty())
712 auto* current_gate = stack.back();
715 if (!previous.empty() && previous.back() == current_gate)
722 visited.insert(current_gate);
725 for (
auto* next_predecessor : current_gate->get_predecessors())
727 auto* predecessor_gate = next_predecessor->get_gate();
731 if (in_reg_lookup.find(predecessor_gate) != in_reg_lookup.end())
733 m_state_inputs.insert(next_predecessor->get_net());
734 state_logic.insert(current_gate);
735 state_logic.insert(previous.begin(), previous.end());
740 if (visited.find(predecessor_gate) == visited.end())
742 stack.push_back(predecessor_gate);
745 else if (state_logic.find(predecessor_gate) != state_logic.end())
747 state_logic.insert(current_gate);
748 state_logic.insert(previous.begin(), previous.end());
755 previous.push_back(current_gate);
764 m_round_logic = sorted_by_id(state_logic);
767 std::set<Net*> visited_nets;
768 for (
auto* gate : m_round_logic)
770 for (
auto* in_net : gate->get_fan_in_nets())
772 if (!visited_nets.insert(in_net).second)
777 if (in_net->get_num_of_sources() != 1)
782 if (m_state_inputs.find(in_net) != m_state_inputs.end())
787 auto* src_gate = in_net->get_sources().at(0)->get_gate();
788 if (state_logic.find(src_gate) != state_logic.end())
793 const u32 num_state_destinations = in_net->get_num_of_destinations([&state_logic](
const Endpoint* ep) {
return state_logic.find(ep->
get_gate()) != state_logic.end(); });
794 if (num_state_destinations > m_size / 2)
796 m_control_inputs.insert(in_net);
800 m_other_inputs.insert(in_net);
811 std::set<Gate*> split_gates;
812 if (m_is_round_based)
814 split_gates.insert(m_out_reg.begin(), m_out_reg.end());
818 if (graph_res.is_error())
820 return ERR(graph_res.get_error());
822 m_graph = std::move(graph_res.get());
824 m_has_round_function =
true;
828 void CipherCandidate::compute_gate_dependencies()
const
830 if (m_has_gate_dependencies)
834 m_has_gate_dependencies =
true;
838 const std::unordered_set<Gate*> out_reg_lookup(m_out_reg.begin(), m_out_reg.end());
839 const std::unordered_set<Gate*> round_logic_lookup(m_round_logic.begin(), m_round_logic.end());
844 for (
auto* in_ff : m_in_reg)
846 std::unordered_set<Gate*> visited = {in_ff};
847 std::vector<Gate*> stack = {in_ff};
848 while (!stack.empty())
850 auto* current_gate = stack.back();
853 m_input_ffs_of_gate[current_gate].insert(in_ff);
855 for (
auto* next_successor : current_gate->get_successors())
857 auto* successor_gate = next_successor->get_gate();
861 if (out_reg_lookup.find(successor_gate) != out_reg_lookup.end())
863 m_input_ffs_of_gate[successor_gate].insert(in_ff);
866 else if (round_logic_lookup.find(successor_gate) != round_logic_lookup.end())
868 if (visited.insert(successor_gate).second)
870 stack.push_back(successor_gate);
881 std::unordered_map<Gate*, u32> in_degree;
882 in_degree.reserve(m_round_logic.size());
883 for (
auto* gate : m_round_logic)
887 for (
auto* gate : m_round_logic)
889 for (
auto* next_successor : gate->get_successors())
891 auto* successor_gate = next_successor->get_gate();
892 if (round_logic_lookup.find(successor_gate) != round_logic_lookup.end())
894 in_degree[successor_gate]++;
900 std::unordered_map<Gate*, u32> distance;
901 distance.reserve(m_round_logic.size());
902 for (
auto* in_ff : m_in_reg)
904 for (
auto* next_successor : in_ff->get_successors())
906 auto* successor_gate = next_successor->get_gate();
907 if (round_logic_lookup.find(successor_gate) != round_logic_lookup.end())
909 distance[successor_gate] = std::max(distance[successor_gate],
u32(1));
915 ordered.reserve(m_round_logic.size());
916 for (
auto* gate : m_round_logic)
918 if (in_degree[gate] == 0)
926 for (
auto* next_successor : gate->get_successors())
928 auto* successor_gate = next_successor->get_gate();
929 if (round_logic_lookup.find(successor_gate) == round_logic_lookup.end())
934 distance[successor_gate] = std::max(distance[successor_gate], distance[gate] + 1);
935 if (--in_degree[successor_gate] == 0)
937 ordered.push_back(successor_gate);
942 if (
ordered.size() != m_round_logic.size())
945 log_warning(
"hawkeye",
"the round function of the candidate is not acyclic, {} of its {} gates were not reached in topological order.", m_round_logic.size() -
ordered.size(), m_round_logic.size());
948 for (
const auto& [gate, gate_distance] : distance)
950 if (gate_distance != 0)
952 m_longest_distance_to_gate[gate_distance].insert(gate);
962 std::map<std::vector<u32>, std::vector<SBox*>> variants_by_input;
963 for (
const auto& sbox : m_sboxes)
966 sbox->identified_as.clear();
967 variants_by_input[ids_of(sbox->input_gates)].push_back(sbox.get());
975 std::vector<const std::vector<SBox*>*> groups;
976 groups.reserve(variants_by_input.size());
977 for (
const auto& [_, variants] : variants_by_input)
979 groups.push_back(&variants);
981 std::stable_sort(groups.begin(), groups.end(), [](
const auto* lhs,
const auto* rhs) { return lhs->front()->input_gates.size() < rhs->front()->input_gates.size(); });
985 u32 num_identified = 0;
986 for (
const auto* group : groups)
988 for (
u32 i = 0; i < group->size(); i++)
990 auto* variant = group->at(i);
992 if (name_res.is_error())
994 return ERR(name_res.get_error());
997 if (name_res.get().empty())
1003 variant->identified_as = name_res.get();
1007 for (
u32 j = i + 1; j < group->size(); j++)
1015 log_info(
"hawkeye",
"identified {} of the {} S-boxes of the candidate.", num_identified, m_sboxes.size());
1016 return OK(num_identified);
1021 if (sbox ==
nullptr)
1023 return ERR(
"S-box is a nullptr");
1026 if (!m_has_round_function)
1028 return ERR(
"round function has not been computed, call build_round_function first");
1033 return ERR(
"S-box has no input or no output gates");
1044 std::vector<BooleanFunction> bfs;
1045 std::set<Net*> all_inputs;
1048 const auto& fan_out_nets = out_gate->get_fan_out_nets();
1049 if (fan_out_nets.size() != 1)
1051 return ERR(
"gate '" + out_gate->get_name() +
"' with ID " + std::to_string(out_gate->get_id())
1052 +
" has none or multiple fan-out nets, which is currently not supported");
1055 auto bf_res = snd.get_subgraph_function(subgraph_gates, fan_out_nets.front(), cache);
1056 if (bf_res.is_error())
1058 return ERR(bf_res.get_error());
1060 bfs.push_back(bf_res.get());
1063 for (
const auto& var : bfs.back().get_variable_names())
1066 if (net_res.is_error())
1068 return ERR(net_res.get_error());
1070 all_inputs.insert(net_res.get());
1075 std::set<Net*> state_inputs, control_inputs, other_inputs;
1076 std::set_intersection(all_inputs.begin(), all_inputs.end(), m_state_inputs.begin(), m_state_inputs.end(), std::inserter(state_inputs, state_inputs.begin()));
1077 std::set_intersection(all_inputs.begin(), all_inputs.end(), m_control_inputs.begin(), m_control_inputs.end(), std::inserter(control_inputs, control_inputs.begin()));
1078 std::set_intersection(all_inputs.begin(), all_inputs.end(), m_other_inputs.begin(), m_other_inputs.end(), std::inserter(other_inputs, other_inputs.begin()));
1080 if (state_inputs.empty() || state_inputs.size() > MAX_SBOX_BITS)
1082 log_info(
"hawkeye",
"skipping an S-box that reads {} state inputs, which is not a supported S-box width.", state_inputs.size());
1083 return OK(std::string());
1086 if (control_inputs.size() > MAX_CONTROL_BITS)
1088 log_info(
"hawkeye",
"skipping an S-box that reads {} control inputs, which is too many to try every assignment of.", control_inputs.size());
1089 return OK(std::string());
1094 for (
auto& bf : bfs)
1096 for (
const auto* other_in : other_inputs)
1099 if (sub_res.is_error())
1101 return ERR(sub_res.get_error());
1113 std::vector<std::string> variable_names;
1114 for (
const auto* n : state_inputs)
1118 for (
const auto* n : control_inputs)
1123 std::vector<std::vector<BooleanFunction::Value>> tables;
1124 tables.reserve(bfs.size());
1125 for (
const auto& bf : bfs)
1127 auto tt_res = bf.compute_truth_table(variable_names);
1128 if (tt_res.is_error())
1130 return ERR(tt_res.get_error());
1132 tables.push_back(std::move(tt_res.get().front()));
1137 const u32 num_state_rows = 1 << state_inputs.size();
1138 for (
u32 assignment = 0; assignment < (1u << control_inputs.size()); assignment++)
1140 std::vector<std::vector<BooleanFunction::Value>> rows(num_state_rows, std::vector<BooleanFunction::Value>(tables.size()));
1141 for (
u32 out = 0; out < tables.size(); out++)
1143 const auto& table = tables.at(out);
1144 for (
u32 row = 0; row < num_state_rows; row++)
1146 rows[row][out] = table.at((
u64(assignment) << state_inputs.size()) + row);
1150 auto name_res = lookup_sbox(rows, state_inputs.size(), db);
1151 if (name_res.is_error())
1153 return ERR(name_res.get_error());
1155 if (!name_res.get().empty())
1157 return OK(name_res.get());
1161 return OK(std::string());
1166 if (output_functions.empty())
1168 return ERR(
"no output functions provided");
1171 std::set<std::string> variables;
1172 for (
const auto& bf : output_functions)
1174 const auto bf_variables = bf.get_variable_names();
1175 variables.insert(bf_variables.begin(), bf_variables.end());
1178 if (variables.empty() || variables.size() > MAX_SBOX_BITS)
1180 return ERR(
"the output functions read " + std::to_string(variables.size()) +
" variables, but an S-box reads between 1 and " + std::to_string(MAX_SBOX_BITS));
1183 const std::vector<std::string> variable_names(variables.begin(), variables.end());
1185 std::vector<std::vector<BooleanFunction::Value>> tables;
1186 tables.reserve(output_functions.size());
1187 for (
const auto& bf : output_functions)
1189 auto tt_res = bf.compute_truth_table(variable_names);
1190 if (tt_res.is_error())
1192 return ERR(tt_res.get_error());
1194 tables.push_back(std::move(tt_res.get().front()));
1197 std::vector<std::vector<BooleanFunction::Value>> rows(1 << variable_names.size(), std::vector<BooleanFunction::Value>(tables.size()));
1198 for (
u32 out = 0; out < tables.size(); out++)
1200 for (
u32 row = 0; row < rows.size(); row++)
1202 rows[row][out] = tables.at(out).at(row);
1206 return lookup_sbox(rows, variable_names.size(), db);
1211 if (m_netlist ==
nullptr)
1213 return ERR(
"candidate is empty");
1217 if (candidate_module ==
nullptr)
1219 return ERR(
"could not create a module for the candidate");
1222 if (m_is_round_based)
1224 if (m_netlist->
create_module(
"state_register", candidate_module, m_in_reg) ==
nullptr)
1226 return ERR(
"could not create a module for the state register of the candidate");
1231 if (m_netlist->
create_module(
"input_register", candidate_module, m_in_reg) ==
nullptr)
1233 return ERR(
"could not create a module for the input register of the candidate");
1235 if (m_netlist->
create_module(
"output_register", candidate_module, m_out_reg) ==
nullptr)
1237 return ERR(
"could not create a module for the output register of the candidate");
1244 std::unordered_set<Gate*> already_in_a_module;
1246 for (
const auto& sbox : m_sboxes)
1253 const auto sbox_gates = sbox->get_combinational_gates();
1254 if (std::any_of(sbox_gates.begin(), sbox_gates.end(), [&already_in_a_module](
Gate* g) { return already_in_a_module.count(g); }))
1256 log_info(
"hawkeye",
"skipping S-box '{}' reading flip-flop {}, as it overlaps an S-box that was already turned into a module.", sbox->identified_as, sbox->input_gates.front()->get_id());
1260 if (m_netlist->
create_module(sbox->identified_as +
"_" + std::to_string(num_sboxes), candidate_module, sbox_gates) ==
nullptr)
1262 return ERR(
"could not create a module for S-box '" + sbox->identified_as +
"' of the candidate");
1265 already_in_a_module.insert(sbox_gates.begin(), sbox_gates.end());
1269 log_info(
"hawkeye",
"created a module for the candidate holding {} S-box modules.", num_sboxes);
1270 return OK(candidate_module);
This file contains the class that holds all information on a candidate for a symmetric cryptographic ...
static constexpr u32 MAX_TRUTH_TABLE_VARIABLES
static BooleanFunction Const(const BooleanFunction::Value &value)
static Result< u64 > to_u64(const std::vector< BooleanFunction::Value > &value)
static Result< Net * > get_net_from(const Netlist *netlist, const BooleanFunction &var)
std::string get_boolean_variable_name() const
Module * get_top_module() const
Module * create_module(const u32 module_id, const std::string &name, Module *parent, const std::vector< Gate * > &gates={})
A directed graph corresponding to a netlist.
static Result< std::unique_ptr< NetlistGraph > > from_gates(const std::vector< Gate * > &gates, const std::set< Gate * > &split_gates={}, const std::function< bool(const Net *)> &filter=nullptr)
Create a directed graph from a subset of the gates of a netlist.
A candidate for a symmetric cryptographic implementation within a netlist.
const std::map< u32, std::set< Gate * > > & get_longest_distance_to_gate() const
Get a map from a distance to all gates reachable within at most that distance from any input flip-flo...
std::vector< Gate * > get_gates() const
Get all gates of the candidate, i.e., its registers together with its round function,...
const std::set< Net * > & get_control_inputs() const
Get the control inputs of the round function.
std::vector< SBox * > get_sboxes() const
Get the S-boxes located within the round function of the candidate.
Result< u32 > identify_sboxes(const SBoxDatabase &db)
Try to identify all S-boxes of the candidate by matching them against a database of known S-boxes.
graph_algorithm::NetlistGraph * get_graph() const
Get the graph of the round function, in which the gates of the state register are represented by a pr...
Result< std::vector< SBox * > > locate_sboxes()
Try to locate S-boxes within the round function of the candidate.
void clear_sboxes()
Discard the S-boxes located so far.
const std::set< Net * > & get_state_outputs() const
Get the state outputs of the round function.
const std::vector< Gate * > & get_input_reg() const
Get the input register of the candidate, ordered by gate ID.
const std::vector< Gate * > & get_round_logic() const
Get the combinational logic computing the next state, ordered by gate ID.
CipherCandidate()=default
Result< Module * > create_modules()
Write the candidate back into the netlist as a module hierarchy.
Result< std::monostate > build_round_function()
Determine the round function of the candidate, i.e., the combinational logic computing the next state...
const std::set< Net * > & get_state_inputs() const
Get the state inputs of the round function.
bool operator==(const CipherCandidate &rhs) const
Check whether two candidates have the same size and the same registers.
Netlist * get_netlist() const
Get the netlist that the candidate belongs to.
const std::set< Net * > & get_other_inputs() const
Get the remaining inputs of the round function.
bool is_round_based() const
Check whether the candidate is round-based, i.e., whether its input and output register are the same.
Result< std::string > identify_sbox(const SBox *sbox, const SBoxDatabase &db) const
Try to identify a single S-box of this candidate by matching it against a database of known S-boxes u...
u32 get_size() const
Get the size of the candidate, i.e., the width of its state register.
bool operator<(const CipherCandidate &rhs) const
Compare two candidates.
const std::vector< Gate * > & get_output_reg() const
Get the output register of the candidate, ordered by gate ID. Equal to the input register for a round...
bool has_round_function() const
Check whether the round function of the candidate has been computed, see build_round_function.
const std::map< Gate *, std::set< Gate * > > & get_input_ffs_of_gate() const
Get a map from each gate of the round function to the input flip-flops it depends on.
Database of known S-boxes.
This file contains functions related to graph components.
std::map< std::pair< u32, const GatePin * >, BooleanFunction > SubgraphFunctionCache
#define log_info(channel,...)
#define log_warning(channel,...)
Result< std::vector< std::vector< u32 > > > get_connected_components(const NetlistGraph *graph, bool strong, u32 min_size=0)
Compute the (strongly) connected components of the specified graph.
Result< std::unique_ptr< NetlistGraph > > get_subgraph(const NetlistGraph *graph, const std::vector< Gate * > &subgraph_gates)
Compute the subgraph induced by the specified gates, including all edges between the corresponding ve...
This file contains the class that holds a netlist graph.
An S-box located within the round function of a CipherCandidate.
std::vector< Gate * > output_gates
The output gates of the S-box, ordered by gate ID. Usually combinational gates feeding the linear lay...
std::vector< Gate * > component
The gates of the connected component that the S-box was located in, including its input flip-flops.
std::vector< Gate * > input_gates
The input flip-flops of the S-box, ordered by gate ID.
std::vector< Gate * > get_combinational_gates() const
Get the combinational gates computing the outputs of the S-box from its input flip-flops.
This file contains functions related to subgraphs.