20 log_info(
"hawkeye",
"start locating S-boxes within round function candidate...");
21 auto start = std::chrono::system_clock::now();
24 const auto* graph = candidate->
get_graph();
28 std::vector<std::vector<Gate*>> res;
29 for (
const auto& c : comps)
31 if (
const auto gates_res = graph->get_gates_from_vertices(c); gates_res.is_ok())
33 res.push_back(gates_res.get());
37 return ERR(gates_res.get_error());
42 if (comp_res.is_error())
44 return ERR(comp_res.get_error());
46 auto components = comp_res.get();
51 std::vector<SBoxCandidate> res;
53 for (
const auto& component : components)
56 std::set<Gate*> component_input_ffs;
57 for (
auto* comp_g : component)
59 if (state_input_reg.find(comp_g) != state_input_reg.end())
61 component_input_ffs.insert(comp_g);
65 u32 number_input_ffs = component_input_ffs.size();
66 if (number_input_ffs < 3)
71 else if (number_input_ffs <= 8)
74 std::set<Gate*> sbox_output_gates;
76 for (
auto* cand_gate : component)
85 const auto suc_gates = cand_gate->get_unique_successors();
86 if (std::none_of(suc_gates.begin(), suc_gates.end(), [&state_output_reg](
Gate* g) { return state_output_reg.find(g) == state_output_reg.end(); }))
88 sbox_output_gates.insert(cand_gate);
93 if (sbox_output_gates.size() == number_input_ffs)
98 sbox_candidate.
m_input_gates = std::move(component_input_ffs);
100 res.push_back(sbox_candidate);
106 std::set<Gate*> current_subset = component_input_ffs;
107 std::vector<std::vector<std::set<Gate*>>> input_groupings;
111 for (
u32 step = 1; step < longest_dist_to_gates.rbegin()->first + 1; step++)
113 if (
const auto dist_it = longest_dist_to_gates.find(step); dist_it != longest_dist_to_gates.end())
115 const auto& new_gates = std::get<1>(*dist_it);
116 current_subset.insert(new_gates.begin(), new_gates.end());
125 if (subgraph_res.is_error())
127 return ERR(subgraph_res.get_error());
129 auto subgraph = std::move(subgraph_res.get());
132 if (comp_res.is_error())
134 return ERR(comp_res.get_error());
139 std::vector<std::vector<Gate*>> subcomponents;
140 std::vector<std::set<Gate*>> input_groups;
141 for (
const auto& comp : comp_res.get())
143 auto gates_res = subgraph->get_gates_from_vertices(comp);
144 if (gates_res.is_error())
146 return ERR(gates_res.get_error());
148 auto comp_gates = gates_res.get();
151 if (std::any_of(comp_gates.begin(), comp_gates.end(), [&component_input_ffs](
Gate* g) { return component_input_ffs.find(g) != component_input_ffs.end(); }))
153 std::set<Gate*> input_group;
154 for (
auto* comp_g : comp_gates)
156 if (state_input_reg.find(comp_g) != state_input_reg.end())
158 input_group.insert(comp_g);
161 lens.insert(input_group.size());
162 input_groups.push_back(std::move(input_group));
163 subcomponents.push_back(std::move(comp_gates));
168 if (lens.size() == 1 && input_groups.at(0).size() > 1 && input_groups.size() > 1)
170 input_groupings.push_back(input_groups);
176 std::vector<std::pair<std::set<Gate*>, std::set<Gate*>>> sboxes_input_output_gates;
177 for (
const auto& input_groups : input_groupings)
179 for (
const auto& input_group : input_groups)
181 std::set<Gate*> output_group;
182 for (
auto* comp_gate : component)
185 if (state_output_reg.find(comp_gate) != state_output_reg.end())
191 if (input_ffs_of_gate.at(comp_gate).size() <= 1)
197 if (!std::includes(input_group.begin(), input_group.end(), input_ffs_of_gate.at(comp_gate).begin(), input_ffs_of_gate.at(comp_gate).end()))
203 auto sucs = comp_gate->get_unique_successors();
204 if (std::all_of(sucs.begin(), sucs.end(), [&input_ffs_of_gate, &input_group](
auto* sg) {
205 return std::includes(input_group.begin(), input_group.end(), input_ffs_of_gate.at(sg).begin(), input_ffs_of_gate.at(sg).end());
212 auto preds = comp_gate->get_unique_predecessors();
215 if (std::includes(output_group.begin(), output_group.end(), preds.begin(), preds.end()))
221 output_group.insert(comp_gate);
225 std::vector<Gate*> to_delete;
226 for (
auto* out_gate : output_group)
228 const auto pred_gates = out_gate->get_unique_predecessors();
229 if (std::all_of(pred_gates.begin(), pred_gates.end(), [output_group](
Gate* g) { return output_group.find(g) != output_group.end(); }))
231 to_delete.push_back(out_gate);
234 for (
auto* del_gate : to_delete)
236 output_group.erase(del_gate);
239 if (input_group.size() <= 8 && output_group.size() <= 20)
241 if (output_group.size() == input_group.size() + 1)
243 for (
auto* drop_gate : output_group)
251 res.push_back(sbox_candidate);
254 else if (output_group.size() == input_group.size() + 2)
256 for (
auto drop_it_1 = output_group.begin(); drop_it_1 != output_group.end(); drop_it_1++)
258 for (
auto drop_it_2 = std::next(drop_it_1); drop_it_2 != output_group.end(); drop_it_2++)
267 res.push_back(sbox_candidate);
278 res.push_back(sbox_candidate);
285 auto duration_in_seconds = std::chrono::duration<double>(std::chrono::system_clock::now() - start).count();
286 log_info(
"hawkeye",
"located {} S-box candidates within round function candidate in {:.2f} seconds", res.size(), duration_in_seconds);
293 log_info(
"hawkeye",
"start identifying S-box candidate...");
294 auto start = std::chrono::system_clock::now();
296 std::string sbox_name;
299 const std::vector<Gate*>& component = sbox_candidate.
m_component;
300 const std::set<Gate*>& input_gates = sbox_candidate.
m_input_gates;
301 const std::set<Gate*>& output_gates = sbox_candidate.
m_output_gates;
303 if (input_gates.size() == 0)
305 return ERR(
"empty set of input gates provided");
311 std::vector<BooleanFunction> bfs;
312 std::set<Net*> all_inputs;
317 for (
const auto* out_gate : output_gates)
320 const auto& fan_out_nets = out_gate->get_fan_out_nets();
321 if (fan_out_nets.size() != 1)
323 log_error(
"hawkeye",
"gate '{}' with ID {} has none or multiple fan-out nets, which is currently not supported", out_gate->get_name(), out_gate->get_id());
325 const auto* out_net = fan_out_nets.front();
328 std::vector<Gate*> subgraph_gates;
329 std::copy_if(component.begin(), component.end(), std::back_inserter(subgraph_gates), [&
in_reg](
Gate* g) { return in_reg.find(g) == in_reg.end(); });
330 auto bf_res = snd.get_subgraph_function(subgraph_gates, out_net, cache);
331 if (bf_res.is_error())
333 return ERR(bf_res.get_error());
335 bfs.push_back(bf_res.get());
338 auto variables = bfs.back().get_variable_names();
340 variables.begin(), variables.end(), std::inserter(all_inputs, all_inputs.end()), [nl](
const std::string& var) { return BooleanFunctionNetDecorator::get_net_from(nl, var).get(); });
344 for (
const auto* in_gate : input_gates)
346 const auto& fan_out_nets = in_gate->get_fan_out_nets();
347 if (fan_out_nets.size() != 1)
349 log_error(
"hawkeye",
"gate '{}' with ID {} has none or multiple fan-out nets, which is currently not supported", in_gate->get_name(), in_gate->get_id());
355 std::set<const Net*> actual_state_inputs;
356 std::set_intersection(all_inputs.begin(), all_inputs.end(), state_inputs.begin(), state_inputs.end(), std::inserter(actual_state_inputs, actual_state_inputs.begin()));
360 std::set<Net*> actual_control_inputs;
361 std::set_intersection(all_inputs.begin(), all_inputs.end(), control_inputs.begin(), control_inputs.end(), std::inserter(actual_control_inputs, actual_control_inputs.begin()));
365 std::set<Net*> actual_other_inputs;
366 std::set_intersection(all_inputs.begin(), all_inputs.end(), other_inputs.begin(), other_inputs.end(), std::inserter(actual_other_inputs, actual_other_inputs.begin()));
369 std::vector<std::string> actual_state_input_names;
370 for (
const auto* n : actual_state_inputs)
375 if (actual_control_inputs.size() <= 8)
379 std::vector<BooleanFunction> bf_const = {bf_const_0, bf_const_1};
384 for (
const auto* other_in : actual_other_inputs)
387 if (sub_res.is_error())
389 return ERR(sub_res.get_error());
397 for (
u32 i = 0; i < (1 << actual_control_inputs.size()); i++)
400 std::map<std::string, BooleanFunction> control_values;
402 for (
auto* ci : actual_control_inputs)
409 std::vector<std::vector<BooleanFunction::Value>> truth_tables_inverted(1 << actual_state_inputs.size(), std::vector<BooleanFunction::Value>(bfs.size()));
410 for (j = 0; j < bfs.size(); j++)
412 const auto& bf = bfs.at(j);
413 const auto tt_res = bf.substitute(control_values).map<std::vector<std::vector<BooleanFunction::Value>>>([&actual_state_input_names](
auto&& bf) {
415 return bf.compute_truth_table(actual_state_input_names);
417 if (tt_res.is_error())
419 return ERR(tt_res.get_error());
422 auto tmp = tt_res.get().front();
423 for (
u32 k = 0; k < tmp.size(); k++)
425 truth_tables_inverted.at(k).at(j) = tmp.at(k);
429 std::vector<u64> sbox_tmp;
430 for (
const auto& tt : truth_tables_inverted)
433 if (u64_res.is_error())
435 return ERR(u64_res.get_error());
437 sbox_tmp.push_back(u64_res.get());
443 if (input_gates.size() != output_gates.size())
445 std::vector<u64> mat;
446 for (j = 0; j < output_gates.size(); j++)
449 for (
u32 k = 0; k < (1 << input_gates.size()); k++)
451 sum += (1 << k) * ((sbox_tmp[k] >> j) & 1);
456 for (j = 0; j < (1 << input_gates.size()); j++)
459 for (
u32 k = 0; k < output_gates.size(); k++)
461 if ((mat.at(k) >> j) & 1 == 1)
468 for (
u32 k = kk + 1; k < output_gates.size(); k++)
470 if ((mat.at(k) >> j) & 1 == 1)
472 mat.at(k) = mat.at(k) ^ mat.at(kk);
477 std::vector<u32> idx;
478 for (j = 0; j < mat.size(); j++)
486 if (idx.size() == input_gates.size())
488 for (j = 0; j < sbox_tmp.size(); j++)
491 for (
u32 k = 0; k < idx.size(); k++)
493 new_val += (1 << k) * ((sbox_tmp.at(j) >> idx.at(k)) & 1);
495 sbox_tmp.at(j) = new_val;
500 log_info(
"hawkeye",
"found {} linear independent", idx.size());
505 std::vector<u8> sbox;
506 for (
const auto elem : sbox_tmp)
508 sbox.push_back((
u8)elem);
511 std::set<u8> sbox_set(sbox.begin(), sbox.end());
512 if (sbox.size() != sbox_set.size())
514 log_info(
"hawkeye",
"found non-bijective S-box");
518 if (
const auto sbox_res = db.
lookup(sbox); sbox_res.is_ok())
520 sbox_name = sbox_res.get();
526 auto duration_in_seconds = std::chrono::duration<double>(std::chrono::system_clock::now() - start).count();
527 if (!sbox_name.empty())
529 log_info(
"hawkeye",
"identified {} S-box in {:.2f} seconds", sbox_name, duration_in_seconds);
533 log_info(
"hawkeye",
"could not identify S-box in {:.2f} seconds", duration_in_seconds);
536 return OK(sbox_name);
static BooleanFunction Const(const BooleanFunction::Value &value)
static Result< u64 > to_u64(const std::vector< BooleanFunction::Value > &value)
std::string get_boolean_variable_name() const
A round candidate constructed from a previously discovered register candidate.
const std::set< Net * > & get_other_inputs() const
Get the candidate's other inputs to the logic computing the next state.
const std::set< Net * > & get_control_inputs() const
Get the candidate's control inputs to the logic computing the next state.
const std::map< Gate *, std::set< Gate * > > & get_input_ffs_of_gate() const
Get a map from each combinational gate of the round function to all the input flip-flops it depends o...
const std::map< u32, std::set< Gate * > > & get_longest_distance_to_gate() const
Get a map from an integer distance to all gates that are reachable within at most that distance when ...
const std::set< Gate * > & get_output_reg() const
Get the candidate's output register.
Netlist * get_netlist() const
Get the netlist of the round candidate. The netlist is a partial copy of the netlist of the register ...
graph_algorithm::NetlistGraph * get_graph() const
Get the netlist graph of the round candidate.
const std::set< Gate * > & get_input_reg() const
Get the candidate's input register.
const std::set< Net * > & get_state_inputs() const
Get the candidate's state inputs to the logic computing the next state.
An S-box candidate discovered within the round function of a round candidate.
std::set< Gate * > m_output_gates
The output gates of the S-box candidate (usually combinational logic that is input to the linear laye...
const RoundCandidate * m_candidate
The RoundCandidate that the S-box candidate belongs to.
std::vector< Gate * > m_component
The gates of the component which the S-box candidate is part of.
std::set< Gate * > m_input_gates
The input gates of the S-box candidate (will be flip-flops).
Database of known S-boxes.
Result< std::string > lookup(const std::vector< u8 > &sbox) const
Attempt to look up an S-box in the database.
This file contains functions related to graph components.
#define log_error(channel,...)
#define log_info(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...
Result< std::vector< SBoxCandidate > > locate_sboxes(const RoundCandidate *candidate)
Try to locate S-box candidates within the combinational next-state logic of the round function candid...
Result< std::string > identify_sbox(const SBoxCandidate &sbox_candidate, const SBoxDatabase &db)
Try to identify an S-box candidate by matching it against a database of known S-boxes under affine eq...
This file contains the class that holds all information on a round candidate.
This file contains a class that holds all information on an S-box candidate as well as the functions ...
This file contains functions related to subgraphs.