17 #include <unordered_map>
18 #include <unordered_set>
31 std::vector<Gate*> sorted_by_id(
const T& gates)
33 std::vector<Gate*> res(gates.begin(), gates.end());
34 std::sort(res.begin(), res.end(), [](
const Gate* lhs,
const Gate* rhs) { return lhs->get_id() < rhs->get_id(); });
38 std::vector<u32> ids_of(
const std::vector<Gate*>& gates)
41 res.reserve(gates.size());
42 for (
const auto* g : gates)
44 res.push_back(g->get_id());
50 constexpr
u32 MAX_SBOX_BITS = 8;
57 constexpr
u32 MAX_CONTROL_BITS = 8;
64 "an S-box tabulated over its state and control inputs must stay within the truth table limit");
67 constexpr
u32 MAX_SBOX_ROWS = 1 << MAX_SBOX_BITS;
78 Result<std::string> lookup_sbox(
const std::vector<std::vector<BooleanFunction::Value>>& rows,
const u32 num_inputs,
const SBoxDatabase& db)
80 if (num_inputs == 0 || num_inputs > MAX_SBOX_BITS)
82 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));
85 const u32 num_rows = 1 << num_inputs;
86 if (rows.size() != num_rows)
88 return ERR(
"expected " + std::to_string(num_rows) +
" rows for " + std::to_string(num_inputs) +
" input bits, got " + std::to_string(rows.size()));
90 const u32 num_outputs = rows.front().size();
92 std::vector<u64> values;
93 values.reserve(num_rows);
94 for (
const auto& row : rows)
97 if (u64_res.is_error())
99 return ERR(u64_res.get_error());
101 values.push_back(u64_res.get());
107 if (num_outputs != num_inputs)
109 std::vector<std::bitset<MAX_SBOX_ROWS>> mat(num_outputs);
110 for (
u32 out = 0; out < num_outputs; out++)
112 for (
u32 row = 0; row < num_rows; row++)
114 mat[out][row] = (values[row] >> out) & 1;
118 for (
u32 row = 0; row < num_rows; row++)
121 for (
u32 out = 0; out < num_outputs; out++)
129 for (
u32 out = pivot + 1; out < num_outputs; out++)
133 mat[out] ^= mat[pivot];
138 std::vector<u32> independent;
139 for (
u32 out = 0; out < num_outputs; out++)
143 independent.push_back(out);
147 if (independent.size() != num_inputs)
149 return OK(std::string());
152 for (
auto& value : values)
155 for (
u32 i = 0; i < independent.size(); i++)
157 reduced |= ((value >> independent.at(i)) & 1) << i;
163 std::vector<u8> sbox;
164 sbox.reserve(values.size());
165 for (
const auto value : values)
167 sbox.push_back((
u8)value);
170 if (std::set<u8>(sbox.begin(), sbox.end()).
size() != sbox.size())
173 return OK(std::string());
176 if (
const auto lookup_res = db.lookup(sbox); lookup_res.is_ok())
178 return OK(lookup_res.get());
180 return OK(std::string());
191 std::unordered_set<Gate*> visited;
193 while (!stack.empty())
195 auto* current_gate = stack.back();
198 if (!visited.insert(current_gate).second)
203 for (
auto* pred_gate : current_gate->get_unique_predecessors())
205 if (in_component.count(pred_gate) && !pred_gate->get_type()->has_property(
GateTypeProperty::ff))
207 stack.push_back(pred_gate);
212 return sorted_by_id(visited);
217 m_in_reg = sorted_by_id(round_reg);
218 m_out_reg = m_in_reg;
219 m_in_reg_ids = ids_of(m_in_reg);
220 m_out_reg_ids = m_in_reg_ids;
221 m_size = m_in_reg.size();
222 m_netlist = m_in_reg.front()->get_netlist();
223 m_is_round_based =
true;
228 m_in_reg = sorted_by_id(
in_reg);
229 m_out_reg = sorted_by_id(
out_reg);
230 m_in_reg_ids = ids_of(m_in_reg);
231 m_out_reg_ids = ids_of(m_out_reg);
232 m_size = m_out_reg.size();
233 m_netlist = m_out_reg.front()->get_netlist();
234 m_is_round_based = m_in_reg == m_out_reg;
242 if (this->m_size != rhs.m_size)
244 return this->m_size > rhs.m_size;
246 if (this->m_in_reg_ids != rhs.m_in_reg_ids)
248 return this->m_in_reg_ids < rhs.m_in_reg_ids;
250 return this->m_out_reg_ids < rhs.m_out_reg_ids;
255 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;
270 return m_is_round_based;
275 return m_has_round_function;
290 return m_round_logic;
295 std::set<Gate*> all(m_in_reg.begin(), m_in_reg.end());
296 all.insert(m_out_reg.begin(), m_out_reg.end());
297 all.insert(m_round_logic.begin(), m_round_logic.end());
298 return sorted_by_id(all);
303 std::vector<SBox*> res;
304 res.reserve(m_sboxes.size());
305 for (
const auto& sbox : m_sboxes)
307 res.push_back(sbox.get());
314 return m_graph.get();
319 return m_state_inputs;
324 return m_control_inputs;
329 return m_other_inputs;
334 return m_state_outputs;
339 compute_gate_dependencies();
340 return m_input_ffs_of_gate;
345 compute_gate_dependencies();
346 return m_longest_distance_to_gate;
356 if (!m_has_round_function)
358 return ERR(
"round function has not been computed, call build_round_function first");
363 if (!m_sboxes.empty())
370 const std::unordered_set<Gate*> in_reg_lookup(m_in_reg.begin(), m_in_reg.end());
371 const std::unordered_set<Gate*> out_reg_lookup(m_out_reg.begin(), m_out_reg.end());
376 const auto candidate_gates =
get_gates();
377 const std::unordered_set<Gate*> candidate_lookup(candidate_gates.begin(), candidate_gates.end());
380 if (comp_res.is_error())
382 return ERR(comp_res.get_error());
385 for (
const auto& component_vertices : comp_res.get())
392 std::set<Gate*> component_input_ffs;
393 std::set<Gate*> component_gates;
394 for (
const u32 vertex : component_vertices)
396 const auto gate_res = m_graph->get_gate_from_vertex(vertex);
397 if (gate_res.is_error())
399 return ERR(gate_res.get_error());
401 auto* g = gate_res.get();
407 component_gates.insert(g);
408 if (!m_graph->is_shadow_vertex(vertex) && in_reg_lookup.count(g))
410 component_input_ffs.insert(g);
414 const std::vector<Gate*> component = sorted_by_id(component_gates);
415 const u32 number_input_ffs = component_input_ffs.size();
417 if (number_input_ffs < 3)
423 if (number_input_ffs <= MAX_SBOX_BITS)
426 std::set<Gate*> sbox_output_gates;
427 for (
auto* cand_gate : component)
436 const auto suc_gates = cand_gate->get_unique_successors();
437 if (std::none_of(suc_gates.begin(), suc_gates.end(), [&](
Gate* g) { return candidate_lookup.count(g) && !out_reg_lookup.count(g); }))
439 sbox_output_gates.insert(cand_gate);
443 if (sbox_output_gates.size() == number_input_ffs)
445 auto sbox = std::make_unique<SBox>();
446 sbox->component = component;
447 sbox->input_gates = sorted_by_id(component_input_ffs);
448 sbox->output_gates = sorted_by_id(sbox_output_gates);
449 m_sboxes.push_back(std::move(sbox));
460 compute_gate_dependencies();
462 std::set<Gate*> current_subset = component_input_ffs;
463 std::vector<std::vector<std::set<Gate*>>> input_groupings;
465 const u32 max_distance = m_longest_distance_to_gate.empty() ? 0 : m_longest_distance_to_gate.rbegin()->first;
466 for (
u32 step = 1; step <= max_distance; step++)
468 const auto dist_it = m_longest_distance_to_gate.find(step);
469 if (dist_it == m_longest_distance_to_gate.end())
474 current_subset.insert(dist_it->second.begin(), dist_it->second.end());
477 if (subgraph_res.is_error())
479 return ERR(subgraph_res.get_error());
481 const auto subgraph = std::move(subgraph_res.get());
484 if (sub_comp_res.is_error())
486 return ERR(sub_comp_res.get_error());
490 std::set<u32> group_sizes;
491 std::vector<std::set<Gate*>> input_groups;
492 for (
const auto& sub_component : sub_comp_res.get())
494 auto gates_res = subgraph->get_gates_from_vertices(sub_component);
495 if (gates_res.is_error())
497 return ERR(gates_res.get_error());
503 std::set<Gate*> input_group;
504 for (
auto* sub_gate : gates_res.get())
506 if (component_input_ffs.count(sub_gate))
508 input_group.insert(sub_gate);
512 if (input_group.empty())
516 group_sizes.insert(input_group.size());
517 input_groups.push_back(std::move(input_group));
522 if (group_sizes.size() == 1 && input_groups.size() > 1 && input_groups.front().size() > 1)
524 input_groupings.push_back(std::move(input_groups));
528 for (
const auto& input_groups : input_groupings)
530 for (
const auto& input_group : input_groups)
534 std::set<Gate*> output_group;
535 for (
auto* comp_gate : component)
537 if (in_reg_lookup.count(comp_gate) || out_reg_lookup.count(comp_gate))
542 const auto ffs_it = m_input_ffs_of_gate.find(comp_gate);
543 if (ffs_it == m_input_ffs_of_gate.end() || ffs_it->second.size() <= 1)
550 if (!std::includes(input_group.begin(), input_group.end(), ffs_it->second.begin(), ffs_it->second.end()))
557 bool feeds_other_inputs =
false;
558 for (
auto* suc_gate : comp_gate->get_unique_successors())
560 const auto suc_it = m_input_ffs_of_gate.find(suc_gate);
561 if (!candidate_lookup.count(suc_gate) || suc_it == m_input_ffs_of_gate.end())
565 if (!std::includes(input_group.begin(), input_group.end(), suc_it->second.begin(), suc_it->second.end()))
567 feeds_other_inputs =
true;
571 if (!feeds_other_inputs)
580 auto preds = comp_gate->get_unique_predecessors();
581 preds.erase(std::remove_if(preds.begin(), preds.end(), [&](
Gate* g) { return !candidate_lookup.count(g); }), preds.end());
582 std::sort(preds.begin(), preds.end());
583 if (std::includes(output_group.begin(), output_group.end(), preds.begin(), preds.end()))
589 output_group.insert(comp_gate);
593 std::vector<Gate*> to_delete;
594 for (
auto* out_gate : output_group)
596 auto pred_gates = out_gate->get_unique_predecessors();
597 pred_gates.erase(std::remove_if(pred_gates.begin(), pred_gates.end(), [&](
Gate* g) { return !candidate_lookup.count(g); }), pred_gates.end());
598 if (std::all_of(pred_gates.begin(), pred_gates.end(), [&output_group](
Gate* g) { return output_group.count(g); }))
600 to_delete.push_back(out_gate);
603 for (
auto* del_gate : to_delete)
605 output_group.erase(del_gate);
608 if (input_group.size() > MAX_SBOX_BITS || output_group.empty() || output_group.size() > 20)
613 const std::vector<Gate*> inputs = sorted_by_id(input_group);
614 const std::vector<Gate*> outputs = sorted_by_id(output_group);
616 auto add_sbox = [&](std::vector<Gate*> output_gates) {
617 auto sbox = std::make_unique<SBox>();
618 sbox->component = component;
619 sbox->input_gates = inputs;
620 sbox->output_gates = std::move(output_gates);
621 m_sboxes.push_back(std::move(sbox));
627 if (outputs.size() == inputs.size() + 1)
629 for (
u32 drop = 0; drop < outputs.size(); drop++)
631 std::vector<Gate*> reduced = outputs;
632 reduced.erase(reduced.begin() + drop);
633 add_sbox(std::move(reduced));
636 else if (outputs.size() == inputs.size() + 2)
638 for (
u32 drop_1 = 0; drop_1 < outputs.size(); drop_1++)
640 for (
u32 drop_2 = drop_1 + 1; drop_2 < outputs.size(); drop_2++)
642 std::vector<Gate*> reduced = outputs;
643 reduced.erase(reduced.begin() + drop_2);
644 reduced.erase(reduced.begin() + drop_1);
645 add_sbox(std::move(reduced));
657 log_info(
"hawkeye",
"located {} S-boxes within the round function of the candidate.", m_sboxes.size());
663 if (m_netlist ==
nullptr)
665 return ERR(
"candidate is empty");
672 m_round_logic.clear();
673 m_state_inputs.clear();
674 m_state_outputs.clear();
675 m_control_inputs.clear();
676 m_other_inputs.clear();
677 m_input_ffs_of_gate.clear();
678 m_longest_distance_to_gate.clear();
679 m_has_gate_dependencies =
false;
681 m_has_round_function =
false;
683 const std::unordered_set<Gate*> in_reg_lookup(m_in_reg.begin(), m_in_reg.end());
684 std::set<Gate*> state_logic;
689 for (
const auto* out_ff : m_out_reg)
692 if (ff_data_predecessors.size() != 1)
698 const auto* pred_ep = ff_data_predecessors.at(0);
699 auto* first_comb_gate = pred_ep->get_gate();
704 m_state_outputs.insert(pred_ep->get_net());
706 std::unordered_set<Gate*> visited;
707 std::vector<Gate*> stack = {first_comb_gate};
708 std::vector<Gate*> previous;
709 while (!stack.empty())
711 auto* current_gate = stack.back();
714 if (!previous.empty() && previous.back() == current_gate)
721 visited.insert(current_gate);
724 for (
auto* next_predecessor : current_gate->get_predecessors())
726 auto* predecessor_gate = next_predecessor->get_gate();
730 if (in_reg_lookup.find(predecessor_gate) != in_reg_lookup.end())
732 m_state_inputs.insert(next_predecessor->get_net());
733 state_logic.insert(current_gate);
734 state_logic.insert(previous.begin(), previous.end());
739 if (visited.find(predecessor_gate) == visited.end())
741 stack.push_back(predecessor_gate);
744 else if (state_logic.find(predecessor_gate) != state_logic.end())
746 state_logic.insert(current_gate);
747 state_logic.insert(previous.begin(), previous.end());
754 previous.push_back(current_gate);
763 m_round_logic = sorted_by_id(state_logic);
766 std::set<Net*> visited_nets;
767 for (
auto* gate : m_round_logic)
769 for (
auto* in_net : gate->get_fan_in_nets())
771 if (!visited_nets.insert(in_net).second)
776 if (in_net->get_num_of_sources() != 1)
781 if (m_state_inputs.find(in_net) != m_state_inputs.end())
786 auto* src_gate = in_net->get_sources().at(0)->get_gate();
787 if (state_logic.find(src_gate) != state_logic.end())
792 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(); });
793 if (num_state_destinations > m_size / 2)
795 m_control_inputs.insert(in_net);
799 m_other_inputs.insert(in_net);
810 std::set<Gate*> split_gates;
811 if (m_is_round_based)
813 split_gates.insert(m_out_reg.begin(), m_out_reg.end());
817 if (graph_res.is_error())
819 return ERR(graph_res.get_error());
821 m_graph = std::move(graph_res.get());
823 m_has_round_function =
true;
827 void CipherCandidate::compute_gate_dependencies()
const
829 if (m_has_gate_dependencies)
833 m_has_gate_dependencies =
true;
837 const std::unordered_set<Gate*> out_reg_lookup(m_out_reg.begin(), m_out_reg.end());
838 const std::unordered_set<Gate*> round_logic_lookup(m_round_logic.begin(), m_round_logic.end());
843 for (
auto* in_ff : m_in_reg)
845 std::unordered_set<Gate*> visited = {in_ff};
846 std::vector<Gate*> stack = {in_ff};
847 while (!stack.empty())
849 auto* current_gate = stack.back();
852 m_input_ffs_of_gate[current_gate].insert(in_ff);
854 for (
auto* next_successor : current_gate->get_successors())
856 auto* successor_gate = next_successor->get_gate();
860 if (out_reg_lookup.find(successor_gate) != out_reg_lookup.end())
862 m_input_ffs_of_gate[successor_gate].insert(in_ff);
865 else if (round_logic_lookup.find(successor_gate) != round_logic_lookup.end())
867 if (visited.insert(successor_gate).second)
869 stack.push_back(successor_gate);
880 std::unordered_map<Gate*, u32> in_degree;
881 in_degree.reserve(m_round_logic.size());
882 for (
auto* gate : m_round_logic)
886 for (
auto* gate : m_round_logic)
888 for (
auto* next_successor : gate->get_successors())
890 auto* successor_gate = next_successor->get_gate();
891 if (round_logic_lookup.find(successor_gate) != round_logic_lookup.end())
893 in_degree[successor_gate]++;
899 std::unordered_map<Gate*, u32> distance;
900 distance.reserve(m_round_logic.size());
901 for (
auto* in_ff : m_in_reg)
903 for (
auto* next_successor : in_ff->get_successors())
905 auto* successor_gate = next_successor->get_gate();
906 if (round_logic_lookup.find(successor_gate) != round_logic_lookup.end())
908 distance[successor_gate] = std::max(distance[successor_gate],
u32(1));
914 ordered.reserve(m_round_logic.size());
915 for (
auto* gate : m_round_logic)
917 if (in_degree[gate] == 0)
925 for (
auto* next_successor : gate->get_successors())
927 auto* successor_gate = next_successor->get_gate();
928 if (round_logic_lookup.find(successor_gate) == round_logic_lookup.end())
933 distance[successor_gate] = std::max(distance[successor_gate], distance[gate] + 1);
934 if (--in_degree[successor_gate] == 0)
936 ordered.push_back(successor_gate);
941 if (
ordered.size() != m_round_logic.size())
944 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());
947 for (
const auto& [gate, gate_distance] : distance)
949 if (gate_distance != 0)
951 m_longest_distance_to_gate[gate_distance].insert(gate);
961 std::map<std::vector<u32>, std::vector<SBox*>> variants_by_input;
962 for (
const auto& sbox : m_sboxes)
965 sbox->identified_as.clear();
966 variants_by_input[ids_of(sbox->input_gates)].push_back(sbox.get());
974 std::vector<const std::vector<SBox*>*> groups;
975 groups.reserve(variants_by_input.size());
976 for (
const auto& [_, variants] : variants_by_input)
978 groups.push_back(&variants);
980 std::stable_sort(groups.begin(), groups.end(), [](
const auto* lhs,
const auto* rhs) { return lhs->front()->input_gates.size() < rhs->front()->input_gates.size(); });
984 u32 num_identified = 0;
985 for (
const auto* group : groups)
987 for (
u32 i = 0; i < group->size(); i++)
989 auto* variant = group->at(i);
991 if (name_res.is_error())
993 return ERR(name_res.get_error());
996 if (name_res.get().empty())
1002 variant->identified_as = name_res.get();
1006 for (
u32 j = i + 1; j < group->size(); j++)
1014 log_info(
"hawkeye",
"identified {} of the {} S-boxes of the candidate.", num_identified, m_sboxes.size());
1015 return OK(num_identified);
1020 if (sbox ==
nullptr)
1022 return ERR(
"S-box is a nullptr");
1025 if (!m_has_round_function)
1027 return ERR(
"round function has not been computed, call build_round_function first");
1032 return ERR(
"S-box has no input or no output gates");
1043 std::vector<BooleanFunction> bfs;
1044 std::set<Net*> all_inputs;
1047 const auto& fan_out_nets = out_gate->get_fan_out_nets();
1048 if (fan_out_nets.size() != 1)
1050 return ERR(
"gate '" + out_gate->get_name() +
"' with ID " + std::to_string(out_gate->get_id())
1051 +
" has none or multiple fan-out nets, which is currently not supported");
1054 auto bf_res = snd.get_subgraph_function(subgraph_gates, fan_out_nets.front(), cache);
1055 if (bf_res.is_error())
1057 return ERR(bf_res.get_error());
1059 bfs.push_back(bf_res.get());
1062 for (
const auto& var : bfs.back().get_variable_names())
1065 if (net_res.is_error())
1067 return ERR(net_res.get_error());
1069 all_inputs.insert(net_res.get());
1074 std::set<Net*> state_inputs, control_inputs, other_inputs;
1075 std::set_intersection(all_inputs.begin(), all_inputs.end(), m_state_inputs.begin(), m_state_inputs.end(), std::inserter(state_inputs, state_inputs.begin()));
1076 std::set_intersection(all_inputs.begin(), all_inputs.end(), m_control_inputs.begin(), m_control_inputs.end(), std::inserter(control_inputs, control_inputs.begin()));
1077 std::set_intersection(all_inputs.begin(), all_inputs.end(), m_other_inputs.begin(), m_other_inputs.end(), std::inserter(other_inputs, other_inputs.begin()));
1079 if (state_inputs.empty() || state_inputs.size() > MAX_SBOX_BITS)
1081 log_info(
"hawkeye",
"skipping an S-box that reads {} state inputs, which is not a supported S-box width.", state_inputs.size());
1082 return OK(std::string());
1085 if (control_inputs.size() > MAX_CONTROL_BITS)
1087 log_info(
"hawkeye",
"skipping an S-box that reads {} control inputs, which is too many to try every assignment of.", control_inputs.size());
1088 return OK(std::string());
1093 for (
auto& bf : bfs)
1095 for (
const auto* other_in : other_inputs)
1098 if (sub_res.is_error())
1100 return ERR(sub_res.get_error());
1112 std::vector<std::string> variable_names;
1113 for (
const auto* n : state_inputs)
1117 for (
const auto* n : control_inputs)
1122 std::vector<std::vector<BooleanFunction::Value>> tables;
1123 tables.reserve(bfs.size());
1124 for (
const auto& bf : bfs)
1126 auto tt_res = bf.compute_truth_table(variable_names);
1127 if (tt_res.is_error())
1129 return ERR(tt_res.get_error());
1131 tables.push_back(std::move(tt_res.get().front()));
1136 const u32 num_state_rows = 1 << state_inputs.size();
1137 for (
u32 assignment = 0; assignment < (1u << control_inputs.size()); assignment++)
1139 std::vector<std::vector<BooleanFunction::Value>> rows(num_state_rows, std::vector<BooleanFunction::Value>(tables.size()));
1140 for (
u32 out = 0; out < tables.size(); out++)
1142 const auto& table = tables.at(out);
1143 for (
u32 row = 0; row < num_state_rows; row++)
1145 rows[row][out] = table.at((
u64(assignment) << state_inputs.size()) + row);
1149 auto name_res = lookup_sbox(rows, state_inputs.size(), db);
1150 if (name_res.is_error())
1152 return ERR(name_res.get_error());
1154 if (!name_res.get().empty())
1156 return OK(name_res.get());
1160 return OK(std::string());
1165 if (output_functions.empty())
1167 return ERR(
"no output functions provided");
1170 std::set<std::string> variables;
1171 for (
const auto& bf : output_functions)
1173 const auto bf_variables = bf.get_variable_names();
1174 variables.insert(bf_variables.begin(), bf_variables.end());
1177 if (variables.empty() || variables.size() > MAX_SBOX_BITS)
1179 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));
1182 const std::vector<std::string> variable_names(variables.begin(), variables.end());
1184 std::vector<std::vector<BooleanFunction::Value>> tables;
1185 tables.reserve(output_functions.size());
1186 for (
const auto& bf : output_functions)
1188 auto tt_res = bf.compute_truth_table(variable_names);
1189 if (tt_res.is_error())
1191 return ERR(tt_res.get_error());
1193 tables.push_back(std::move(tt_res.get().front()));
1196 std::vector<std::vector<BooleanFunction::Value>> rows(1 << variable_names.size(), std::vector<BooleanFunction::Value>(tables.size()));
1197 for (
u32 out = 0; out < tables.size(); out++)
1199 for (
u32 row = 0; row < rows.size(); row++)
1201 rows[row][out] = tables.at(out).at(row);
1205 return lookup_sbox(rows, variable_names.size(), db);
1210 if (m_netlist ==
nullptr)
1212 return ERR(
"candidate is empty");
1216 if (candidate_module ==
nullptr)
1218 return ERR(
"could not create a module for the candidate");
1221 if (m_is_round_based)
1223 if (m_netlist->
create_module(
"state_register", candidate_module, m_in_reg) ==
nullptr)
1225 return ERR(
"could not create a module for the state register of the candidate");
1230 if (m_netlist->
create_module(
"input_register", candidate_module, m_in_reg) ==
nullptr)
1232 return ERR(
"could not create a module for the input register of the candidate");
1234 if (m_netlist->
create_module(
"output_register", candidate_module, m_out_reg) ==
nullptr)
1236 return ERR(
"could not create a module for the output register of the candidate");
1243 std::unordered_set<Gate*> already_in_a_module;
1245 for (
const auto& sbox : m_sboxes)
1252 const auto sbox_gates = sbox->get_combinational_gates();
1253 if (std::any_of(sbox_gates.begin(), sbox_gates.end(), [&already_in_a_module](
Gate* g) { return already_in_a_module.count(g); }))
1255 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());
1259 if (m_netlist->
create_module(sbox->identified_as +
"_" + std::to_string(num_sboxes), candidate_module, sbox_gates) ==
nullptr)
1261 return ERR(
"could not create a module for S-box '" + sbox->identified_as +
"' of the candidate");
1264 already_in_a_module.insert(sbox_gates.begin(), sbox_gates.end());
1268 log_info(
"hawkeye",
"created a module for the candidate holding {} S-box modules.", num_sboxes);
1269 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.
#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.