8 #include "pybind11/operators.h"
9 #include "pybind11/pybind11.h"
10 #include "pybind11/stl.h"
11 #include "pybind11/stl_bind.h"
13 namespace py = pybind11;
21 #ifdef PYBIND11_MODULE
22 PYBIND11_MODULE(hawkeye, m)
24 m.doc() =
"Automated tool to locate arbitrary symmetric cryptographic implementations in gate-level netlists.";
28 py::module m(
"hawkeye",
"Automated tool to locate arbitrary symmetric cryptographic implementations in gate-level netlists.");
31 py::class_<HawkeyePlugin, RawPtrWrapper<HawkeyePlugin>,
BasePluginInterface> py_hawkeye_plugin(
32 m,
"HawkeyePlugin", R
"(This class provides an interface to integrate the HAWKEYE tool as a plugin within the HAL framework.)");
35 The name of the plugin.
41 Get the name of the plugin.
43 :returns: The name of the plugin.
48 The version of the plugin.
54 Get the version of the plugin.
56 :returns: The version of the plugin.
61 The description of the plugin.
67 Get the description of the plugin.
69 :returns: The description of the plugin.
74 A set of plugin names that this plugin depends on.
80 Get a set of plugin names that this plugin depends on.
82 :returns: A set of plugin names that this plugin depends on.
86 py::class_<hawkeye::SBoxDatabase, RawPtrWrapper<hawkeye::SBoxDatabase>> py_hawkeye_sbox_database(m, "SBoxDatabase", R
"(
87 This class holds and manages known S-boxes and allows to perform efficient S-box lookups in the database.
90 py_hawkeye_sbox_database.def(py::init<>(), R"(
91 Construct an empty S-box database.
94 py_hawkeye_sbox_database.def(py::init<const std::map<std::string, std::vector<u8>>&>(), py::arg(
"sboxes"), R
"(
95 Construct an S-box database from the given S-boxes.
97 :param dict[str,list[int]] sboxes: A dict from S-box name to the respective S-box.
100 py_hawkeye_sbox_database.def_static(
102 [](
const std::filesystem::path& file_path) -> std::optional<hawkeye::SBoxDatabase> {
110 log_error(
"python_context",
"{}", res.get_error().get());
114 py::arg(
"file_path"),
116 Construct an S-box database from file.
118 :param pathlib.Path file_path: The path from which to load the S-box database file.
119 :returns: The S-box database on success, ``None`` otherwise.
120 :rtype: hawkeye.SBoxDatabase or None
124 Compute the linear representative of the given S-box.
126 :param list[int] sbox: The S-box.
127 :returns: The linear representative.
131 py_hawkeye_sbox_database.def(
134 auto res =
self.add(
name, sbox);
141 log_error(
"python_context",
"{}", res.get_error().get());
148 Add an S-box to the database.
150 :param str name: The name of the S-box.
151 :patam list[int] sbox: The S-box.
152 :returns: ``True`` on success, ``False`` otherwise.
156 py_hawkeye_sbox_database.def(
159 auto res =
self.add(sboxes);
166 log_error(
"python_context",
"{}", res.get_error().get());
172 Add multiple S-boxes to the database.
174 :param dict[str,list[int]] sboxes: A dict from S-box name to the respective S-box.
175 :returns: ``True`` on success, ``False`` otherwise.
179 py_hawkeye_sbox_database.def(
181 [](
hawkeye::SBoxDatabase&
self,
const std::filesystem::path& file_path,
bool overwrite =
false) ->
bool {
182 auto res =
self.load(file_path, overwrite);
189 log_error(
"python_context",
"{}", res.get_error().get());
193 py::arg(
"file_path"),
194 py::arg(
"overwrite") =
false,
196 Load S-boxes from a file and add them to the existing database.
198 :param pathlib.Path file_path: The path from which to load the S-box database file.
199 :param bool overwrite: Set ``True`` to overwrite existing database, ``False`` otherwise. Defaults to ``False``.
200 :returns: ``True`` on success, ``False`` otherwise.
204 py_hawkeye_sbox_database.def(
207 auto res =
self.store(file_path);
214 log_error(
"python_context",
"{}", res.get_error().get());
218 py::arg(
"file_path"),
220 Store the S-box database to a database file.
222 :param pathlib.Path file_path: The path to where to store the S-box database file.
223 :returns: ``True`` on success, ``False`` otherwise.
227 py_hawkeye_sbox_database.def(
230 auto res =
self.lookup(sbox);
237 log_error(
"python_context",
"{}", res.get_error().get());
243 Attempt to look up an S-box in the database.
245 :param list[int] sbox: The S-box to look for.
246 :returns: The S-box name on success, ``None`` otherwise.
254 py::class_<hawkeye::DetectionConfiguration, RawPtrWrapper<hawkeye::DetectionConfiguration>> py_hawkeye_detection_configuration(
255 m, "DetectionConfiguration", R
"(This class holds important parameters that configure the candidate search of HAWKEYE.)");
257 py_hawkeye_detection_configuration.def(py::init<>(), R"(
258 Constructs a default DetectionConfiguration.
261 py::enum_<hawkeye::DetectionConfiguration::Control> py_hawkeye_detection_configuration_control(
262 py_hawkeye_detection_configuration,
264 R
"(This enum specifies the checks that are to be performed on the flip-flops of the netlist to determine whether there should be an edge between two flip-flops or not.)");
266 py_hawkeye_detection_configuration_control
269 R
"(If two flip-flops ``ff1`` and ``ff2`` are connected through combinational logic, an edge is added such that ``(ff1,ff2)`` is part of the graph.)")
272 R
"(If two flip-flops ``ff1`` and ``ff2`` are connected through combinational logic and are of the same gate type, an edge is added such that ``(ff1,ff2)`` is part of the graph.)")
276 R
"(If two flip-flops ``ff1`` and ``ff2`` are connected through combinational logic and are controlled through the same input pins, an edge is added such that ``(ff1,ff2)`` is part of the graph.)")
280 R
"(If two flip-flops ``ff1`` and ``ff2`` are connected through combinational logic and are controlled through the same input nets, an edge is added such that ``(ff1,ff2)`` is part of the graph.)")
284 Checks to be performed on flip-flop control inputs during candidate search.
286 :type: hawkeye.DetectionConfiguration.Control
289 py::enum_<hawkeye::DetectionConfiguration::Components> py_hawkeye_detection_configuration_components(py_hawkeye_detection_configuration,
292 This enum specifies whether SCC detection should be used to refine the results of neighborhood discovery. If SCC detection is used, the exploration only stops if the size of the largest discovered SCC saturates. Specifically, it does no longer require the size of the entire neighborhood to saturate.
295 py_hawkeye_detection_configuration_components
301 Determines whether to use SCC detection as part of neighborhood discovery.
303 :type: hawkeye.DetectionConfiguration.Components
307 A list of a list of gate types that are treated as identical types by the candidate search, i.e., when checking equality of the types of two gates that are different but declared equivalent, ``True`` is returned.
309 :type: list[list[str]]
313 Neighborhood discovery iteration timeout.
319 Minimum number of flip-flops for a register candidate to be created.
324 py::class_<hawkeye::RegisterCandidate, RawPtrWrapper<hawkeye::RegisterCandidate>> py_hawkeye_register_candidate(m, "RegisterCandidate", R
"(
325 This class holds all information belonging to a register candidate discovered by HAWKEYE's candidate search and makes these information accessible through getters.
328 py_hawkeye_register_candidate.def(py::init<>(), R"(Default constructor for ``RegisterCandidate``.)");
330 py_hawkeye_register_candidate.def(py::init<const std::set<Gate*>&>(), py::arg(
"round_reg"), R
"(
331 Construct a state register candidate from the state register of a round-based implementation.
333 :param set[hal_py.Gate] round_reg: The state register.
336 py_hawkeye_register_candidate.def(py::init<const std::set<Gate*>&,
const std::set<Gate*>&>(), py::arg(
"in_reg"), py::arg(
"out_reg"), R
"(
337 Construct a state register candidate from the input and output registers from one round of a pipelined implementation.
339 :param set[hal_py.Gate] in_reg: The input register.
340 :param set[hal_py.Gate] out_reg: The output register.
344 Get the netlist associated with the candidate.
346 :returns: The netlist of the candidate.
347 :rtype: hal_py.Netlist
351 Get the size of the candidate, i.e., the width of its registers.
353 :returns: The size of the candidate.
358 Check if the candidate is round-based, i.e., input and output register are the same.
360 :returns: ``True`` if the candidate is round-based, ``False`` otherwise.
365 Get the candidate's input register.
367 :returns: The input register of the candidate.
368 :rtype: set[hal_py.Gate]
372 Get the candidate's output register.
374 :returns: The output register of the candidate.
375 :rtype: set[hal_py.Gate]
378 py::class_<hawkeye::RoundCandidate, RawPtrWrapper<hawkeye::RoundCandidate>> py_hawkeye_round_candidate(m, "RoundCandidate", R
"(
379 This class holds all information belonging to a round candidate. Round candidates are constructed from register candidates by copying the sub-circuit consisting of the input and (if pipelined) output registers as well as the next-state/round-function logic in between these registers.
380 For round-based implementations, commonly only a single register exists that acts as an input and output register at the same time.
381 In such cases, this register is considered to be the input register of the round function and an exact copy of the register will be appended to the round function outputs so that input and output register are guaranteed to be distinct.
384 py_hawkeye_round_candidate.def(py::init<>(), R"(Default constructor for ``RoundCandidate``.)");
386 py_hawkeye_round_candidate.def_static(
387 "from_register_candidate",
396 log_error(
"python_context",
"{}", res.get_error().get());
400 py::arg(
"candidate"),
402 Compute a round candidate from a previously identified register candidate.
403 The netlist of this candidate will be a partial copy of the original netlist, comprising only the gates belonging to the registers and the logic computing the next state.
404 In case of a round-based implementation, the output register will be a copy of the input register.
405 All data structures of the round candidate will be initialized in the process.
407 :param hawkeye.RegisterCandidate candidate: The register candidate.
408 :returns: The round candidate on success, ``None`` otherwise.
409 :rtype: hawkeye.RoundCandidate or None
413 Get the netlist of the round candidate. The netlist is a partial copy of the netlist of the register candidate.
415 :returns: The netlist of the candidate.
416 :rtype: hal_py.Netlist
420 Get the netlist graph of the round candidate.
422 :returns: The netlist graph of the candidate.
423 :rtype: graph_algorithm.NetlistGraph
427 Get the size of the candidate, i.e., the width of its registers.
429 :returns: The size of the candidate.
434 Get the candidate's input register.
436 :returns: The input register of the candidate.
437 :rtype: set[hal_py.Gate]
441 Get the candidate's output register.
443 :returns: The output register of the candidate.
444 :rtype: set[hal_py.Gate]
448 Get the candidate's combinational logic computing the next state.
450 :returns: The state logic of the candidate.
451 :rtype: set[hal_py.Gate]
455 Get the candidate's state inputs to the logic computing the next state.
457 :returns: The state inputs of the candidate.
458 :rtype: set[hal_py.Net]
462 Get the candidate's control inputs to the logic computing the next state.
464 :returns: The control inputs of the candidate.
465 :rtype: set[hal_py.Net]
469 Get the candidate's other inputs to the logic computing the next state.
471 :returns: The other inputs of the candidate.
472 :rtype: set[hal_py.Net]
476 Get the candidate's state outputs from the logic computing the next state.
478 :returns: The state outputs of the candidate.
479 :rtype: set[hal_py.Net]
483 Get a dict from each combinational gate of the round function to all the input flip-flops it depends on.
485 :returns: A dict from gates to sets of input flip-flops.
486 :rtype: dict[hal_py.Gate,set[hal_py.Gate]]
490 Get a dict from an integer distance to all gates that are reachable within at most that distance when starting at any input flip-flop.
492 :returns: A dict from longest distance to a set of gates being reachable in at most that distance.
493 :rtype: dict[int,set[hal_py.Gate]]
498 [](
Netlist* nl,
const std::vector<hawkeye::DetectionConfiguration>& configs,
u32 min_state_size = 40,
const std::vector<Gate*>& start_ffs = {})
499 -> std::optional<std::vector<hawkeye::RegisterCandidate>> {
507 log_error(
"python_context",
"cannot detect crypto candidates:\n{}", res.get_error().get());
513 py::arg(
"min_state_size") = 40,
514 py::arg(
"start_ffs") = std::vector<Gate*>(),
516 Attempt to locate candidates for symmetric cryptographic implementations within a gate-level netlist.
517 Search operates only on an abstraction of the netlist that contains only flip-flops as nodes and connections through combinational logic as edges.
518 The algorithm computes the k-neighborhood of each flip-flop for ``k = 1, ..., config.timeout`` and stops when the neighborhood size saturates.
519 Depending on the ``config``, additional criteria are used to narrow down the search space, see ``DetectionConfiguration.Control`` and ``DetectionConfiguration.Components`` for details.
520 When the neighborhood size saturates, a register candidate is created if the last neighborhood size is larger than ``config.min_register_size``.
521 After the candidates have been identified, they are reduced further to produce the final set of register candidates.
522 To this end, large candidates that fully contain a smaller candidate and candidates that are smaller than ``min_state_size`` are discarded.
524 :param hal_py.Netlist nl: The netlist to operate on.
525 :param list[hawkeye.DetectionConfiguration] configs: The configurations of the detection approaches to be executed one after another on each start flip-flop.
526 :param int min_state_size: The minimum size of a register candidate to be considered a cryptographic state register. Defaults to ``40``.
527 :param list[hal_py.Gate] start_ffs: The flip-flops to analyze. Defaults to an empty list, i.e., all flip-flops in the netlist will be analyzed.
528 :returns: A list of candidates on success, ``None`` otherwise.
529 :rtype: list[hawkeye.RegisterCandidate] or None
532 py::class_<hawkeye::SBoxCandidate, RawPtrWrapper<hawkeye::SBoxCandidate>> py_hawkeye_sbox_candidate(
535 R
"(This class stores all information related to an S-box candidate discovered within the round function of a round candidate, such as the ``RoundCandidate`` it belongs to, the connected component it is part of, and its input and output gates.)");
537 py_hawkeye_sbox_candidate.def(py::init<>(), R"(
538 Default constructor for ``SBoxCandidate``.
547 py_hawkeye_sbox_candidate.def_readonly(
560 log_error(
"python_context",
"cannot locate S-boxes:\n{}", res.get_error().get());
564 py::arg(
"candidate"),
566 Try to locate S-box candidates within the combinational next-state logic of the round function candidate.
567 Computes an initial set of connected components within the round function extracted between the input and output register of the round candidate.
568 If these initial components are reasonably small and their input and output sizes match, construct S-box candidates for further analysis right away.
569 Otherwise, iteratively consider more combinational gates starting from the components' input gates and search for sub-components.
570 Create S-box candidates for these sub-components after determining the respective S-box output gates.
572 :param hawkeye.RoundCandidate candidate: A round function candidate.
573 :returns: A list of S-box candidates on success, ``None`` otherwise.
574 :rtype: list[hawkeye.SBoxCandidate] or None
587 log_error(
"python_context",
"cannot identify S-box:\n{}", res.get_error().get());
591 py::arg(
"sbox_candidate"),
594 Try to identify an S-box candidate by matching it against a database of known S-boxes under affine equivalence.
596 Note that a candidate which simply does not match any S-box of the database is not an error: in that case an empty string is returned. ``None`` is only returned if the candidate could not be analyzed at all.
598 :param hawkeye.SBoxCandidate sbox_candidate: An S-box candidate.
599 :param hawkeye.SBoxDatabase db: A database of known S-boxes.
600 :returns: The name of the matching S-box, or an empty string if no S-box of the database matched. ``None`` on error.
604 #ifndef PYBIND11_MODULE
This file contains the function for HAWKEYE's candidate search as well as a struct for configuring th...
std::string get_name() const override
Get the name of the plugin.
std::set< std::string > get_dependencies() const override
Get the plugin dependencies.
std::string get_description() const override
Get a short description of the plugin.
std::string get_version() const override
Get the version of the plugin.
A register candidate discovered by HAWKEYE.
Netlist * get_netlist() const
Get the netlist associated with the candidate.
bool is_round_based() const
Check if the candidate is round-based, i.e., input and output register are the same.
u32 get_size() const
Get the size of the candidate, i.e., the width of its registers.
const std::set< Gate * > & get_input_reg() const
Get the candidate's input register.
const std::set< Gate * > & get_output_reg() const
Get the candidate's output register.
A round candidate constructed from a previously discovered register candidate.
const std::set< Gate * > & get_state_logic() const
Get the candidate's combinational logic computing the next state.
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.
u32 get_size() const
Get the size of the candidate, i.e., the width of its registers.
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::set< Net * > & get_state_outputs() const
Get the candidate's state outputs from the logic computing the next state.
static Result< std::unique_ptr< RoundCandidate > > from_register_candidate(RegisterCandidate *candidate)
Compute a round candidate from a previously identified register candidate.
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.
static Result< SBoxDatabase > from_file(const std::filesystem::path &file_path)
Construct an S-box database from file.
void print() const
Print the database.
static std::vector< u8 > compute_linear_representative(const std::vector< u8 > &sbox)
Compute the linear representative of the given S-box.
#define log_error(channel,...)
const Module * module(const Gate *g, const NodeBoxes &boxes)
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::vector< RegisterCandidate > > detect_candidates(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 implementations within a gate-level netlist.
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 all functions related to the HAL plugin API.
This file contains the class that holds all information on a round candidate.
This file contains the S-box database class that holds and manages known cryptographic S-boxes up to ...
This file contains a class that holds all information on an S-box candidate as well as the functions ...
u32 min_register_size
Minimum number of flip-flops for a register candidate to be created.
enum hal::hawkeye::DetectionConfiguration::Components components
@ 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...
std::vector< std::vector< std::string > > equivalent_types
A vector of a vector of gate types that are treated as identical types by the candidate search,...
enum hal::hawkeye::DetectionConfiguration::Control control
@ 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.
u32 timeout
Neighborhood discovery iteration timeout.