HAWKEYE

Automated tool to locate arbitrary symmetric cryptographic implementations in gate-level netlists.

hawkeye.identify_sbox(output_functions: List[hal_py.BooleanFunction], db: hawkeye.SBoxDatabase) Optional[str]

Try to identify an S-box given as one Boolean function per output bit by matching it against a database of known S-boxes under affine equivalence.

Evaluates the Boolean functions into a truth table and looks that up in the database, so it works on functions that do not come from a netlist at all. Every variable occurring in them is taken as an input bit of the S-box, so substitute anything that is not one beforehand.

Note that an S-box which simply does not match anything in the database is not an error: in that case an empty string is returned. None is only returned if the S-box could not be analyzed at all.

Parameters
  • output_functions (list[hal_py.BooleanFunction]) – The Boolean functions of the S-box, one per output bit. Their order does not matter, as affine equivalence absorbs a permutation of the output bits.

  • db (hawkeye.SBoxDatabase) – The database of known S-boxes.

Returns

The name of the matching S-box, or an empty string if no S-box of the database matched. None on error.

Return type

str or None

class hawkeye.DetectionConfiguration

This class holds important parameters that configure the candidate search of HAWKEYE, see CipherCandidate.detect.

__init__(self: hawkeye.DetectionConfiguration) None

Constructs a default DetectionConfiguration.

class Components

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.

Members:

NONE : Do not use SCC detection and instead resort to the simple neighborhood discovery algorithm.

CHECK_SCC : Use SCC detection within the currently explored neighborhood of a start flip-flop.

property name
class Control

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.

Members:

CHECK_FF : 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.

CHECK_TYPE : 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.

CHECK_PINS : 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.

CHECK_NETS : 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.

property name
property components

Determines whether to use SCC detection as part of neighborhood discovery.

Type

hawkeye.DetectionConfiguration.Components

property control

Checks to be performed on flip-flop control inputs during candidate search.

Type

hawkeye.DetectionConfiguration.Control

property equivalent_types

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.

Type

list[list[str]]

property min_register_size

Minimum number of flip-flops of a register for a candidate to be created from it.

Type

int

property timeout

Neighborhood discovery iteration timeout.

Type

int

class hawkeye.CipherCandidate

A candidate for a symmetric cryptographic implementation within a netlist.

A candidate is discovered by detect in stages and is filled in as the analysis proceeds: detection only establishes the state register, build_round_function adds the combinational logic computing the next state, and locate_sboxes adds the S-boxes within that logic. Use has_round_function and get_sboxes to find out how far a candidate has been analyzed.

All gates and nets of a candidate belong to the netlist it was detected in, so they can be inspected and grouped into modules directly.

__init__(*args, **kwargs)

Overloaded function.

  1. __init__(self: hawkeye.CipherCandidate) -> None

Default constructor for CipherCandidate.

  1. __init__(self: hawkeye.CipherCandidate, round_reg: Set[hal_py.Gate]) -> None

    Construct a round-based candidate, i.e., one whose input and output register are the same.

    param set[hal_py.Gate] round_reg

    The state register of the candidate.

  2. __init__(self: hawkeye.CipherCandidate, in_reg: Set[hal_py.Gate], out_reg: Set[hal_py.Gate]) -> None

    Construct a candidate from an input and an output register. The candidate is round-based if both registers are equal.

    param set[hal_py.Gate] in_reg

    The input register of the candidate.

    param set[hal_py.Gate] out_reg

    The output register of the candidate.

build_round_function(self: hawkeye.CipherCandidate) bool

Determine the round function of the candidate, i.e., the combinational logic computing the next state.

Determines the state logic between the input and the output register together with the state, control, and other inputs of the candidate, and builds the graph that locate_sboxes operates on. Discards any S-boxes located so far, as they are derived from the round function, which invalidates all S-boxes previously returned by locate_sboxes and get_sboxes.

Recomputes the round function on every call, which only makes a difference if the netlist changed in the meantime.

Returns

True on success, False otherwise.

Return type

bool

clear_sboxes(self: hawkeye.CipherCandidate) None

Discard the S-boxes located so far.

Invalidates all S-boxes previously returned by locate_sboxes and get_sboxes.

create_modules(self: hawkeye.CipherCandidate) hal_py.Module

Write the candidate back into the netlist as a module hierarchy.

Creates one module holding the entire candidate, a submodule holding its state register, and one submodule per identified S-box holding its combinational gates. S-boxes that were not identified are skipped, as are identified S-boxes that overlap an S-box module already created, since a gate belongs to exactly one module. Every skipped S-box is reported to the log.

Returns

The module holding the candidate on success, None otherwise.

Return type

hal_py.Module or None

static detect(nl: hal_py.Netlist, configs: List[hawkeye.DetectionConfiguration], min_state_size: int = 40, start_ffs: List[hal_py.Gate] = []) Optional[List[hawkeye.CipherCandidate]]

Attempt to locate candidates for symmetric cryptographic SPN, Feistel, and ARX implementations within a gate-level netlist.

Operates on an abstraction of the netlist that holds only the flip-flops as vertices and their connections through combinational logic as edges. Computes the k-neighborhood of every flip-flop for k = 1, ..., config.timeout and stops once the size of the neighborhood saturates, at which point a candidate is created if the neighborhood is larger than config.min_register_size. Depending on the config, further criteria narrow down the search, see DetectionConfiguration.Control and DetectionConfiguration.Components. The candidates found are then reduced by discarding those smaller than min_state_size as well as those that fully contain a smaller candidate.

The returned candidates only know their state register, call build_round_function on a candidate to analyze it further.

Parameters
  • nl (hal_py.Netlist) – The netlist to operate on.

  • configs (list[hawkeye.DetectionConfiguration]) – The configurations of the detection approaches to be executed one after another on each start flip-flop.

  • min_state_size (int) – The minimum size of a candidate to be considered a cryptographic state register. Defaults to 40.

  • start_ffs (list[hal_py.Gate]) – The flip-flops to analyze. Defaults to an empty list, i.e., all flip-flops of the netlist are analyzed.

Returns

A list of candidates on success, None otherwise.

Return type

list[hawkeye.CipherCandidate] or None

get_control_inputs(self: hawkeye.CipherCandidate) Set[hal_py.Net]

Get the control inputs of the round function.

Returns

The control inputs of the candidate.

Return type

set[hal_py.Net]

get_gates(self: hawkeye.CipherCandidate) List[hal_py.Gate]

Get all gates of the candidate, i.e., its registers together with its round function, ordered by gate ID.

Returns

The gates of the candidate.

Return type

list[hal_py.Gate]

get_graph(self: hawkeye.CipherCandidate) graph_algorithm.NetlistGraph

Get the graph of the round function, in which the gates of the state register are represented by a primary and a shadow vertex so that the feedback of a round-based candidate does not close a cycle.

Returns

The graph of the round function, None if the round function has not been computed yet.

Return type

graph_algorithm.NetlistGraph or None

get_input_ffs_of_gate(self: hawkeye.CipherCandidate) Dict[hal_py.Gate, Set[hal_py.Gate]]

Get a dict from each gate of the round function to the input flip-flops it depends on.

Returns

A dict from gates to sets of input flip-flops.

Return type

dict[hal_py.Gate,set[hal_py.Gate]]

get_input_reg(self: hawkeye.CipherCandidate) List[hal_py.Gate]

Get the input register of the candidate, ordered by gate ID.

Returns

The input register of the candidate.

Return type

list[hal_py.Gate]

get_longest_distance_to_gate(self: hawkeye.CipherCandidate) Dict[int, Set[hal_py.Gate]]

Get a dict from a distance to all gates reachable within at most that distance from any input flip-flop.

Returns

A dict from longest distance to a set of gates.

Return type

dict[int,set[hal_py.Gate]]

get_netlist(self: hawkeye.CipherCandidate) hal_py.Netlist

Get the netlist that the candidate belongs to.

Returns

The netlist of the candidate.

Return type

hal_py.Netlist

get_other_inputs(self: hawkeye.CipherCandidate) Set[hal_py.Net]

Get the remaining inputs of the round function.

Returns

The other inputs of the candidate.

Return type

set[hal_py.Net]

get_output_reg(self: hawkeye.CipherCandidate) List[hal_py.Gate]

Get the output register of the candidate, ordered by gate ID. Equal to the input register for a round-based candidate.

Returns

The output register of the candidate.

Return type

list[hal_py.Gate]

get_round_logic(self: hawkeye.CipherCandidate) List[hal_py.Gate]

Get the combinational logic computing the next state, ordered by gate ID.

Returns

The round function of the candidate, empty if it has not been computed yet.

Return type

list[hal_py.Gate]

get_sboxes(self: hawkeye.CipherCandidate) List[hawkeye.SBox]

Get the S-boxes located within the round function of the candidate.

Returns

The S-boxes of the candidate, empty if they have not been located yet.

Return type

list[hawkeye.SBox]

get_size(self: hawkeye.CipherCandidate) int

Get the size of the candidate, i.e., the width of its state register.

Returns

The size of the candidate.

Return type

int

get_state_inputs(self: hawkeye.CipherCandidate) Set[hal_py.Net]

Get the state inputs of the round function.

Returns

The state inputs of the candidate.

Return type

set[hal_py.Net]

get_state_outputs(self: hawkeye.CipherCandidate) Set[hal_py.Net]

Get the state outputs of the round function.

Returns

The state outputs of the candidate.

Return type

set[hal_py.Net]

has_round_function(self: hawkeye.CipherCandidate) bool

Check whether the round function of the candidate has been computed, see build_round_function.

Returns

True if the round function has been computed, False otherwise.

Return type

bool

identify_sbox(self: hawkeye.CipherCandidate, sbox: hawkeye.SBox, db: hawkeye.SBoxDatabase) Optional[str]

Try to identify a single S-box of this candidate by matching it against a database of known S-boxes under affine equivalence.

Tries every assignment of the control inputs that the S-box reads, as the round function computes the S-box for one of them and something else for the others, and the right one is not known in advance. The remaining inputs are held at 0.

Does not annotate the S-box, use identify_sboxes for that.

Note that an S-box which simply does not match anything in the database is not an error: in that case an empty string is returned. None is only returned if the S-box could not be analyzed at all.

Parameters
Returns

The name of the matching S-box, or an empty string if no S-box of the database matched. None on error.

Return type

str or None

identify_sboxes(self: hawkeye.CipherCandidate, db: hawkeye.SBoxDatabase) Optional[int]

Try to identify all S-boxes of the candidate by matching them against a database of known S-boxes.

Annotates every S-box with the outcome, see SBox.status and SBox.identified_as. An S-box that is not contained in the database is not an error.

Since the exact outputs of an S-box are not known in advance, locate_sboxes produces many variants of the same S-box that differ only in which of the surplus gates are taken as its outputs but all read the same input flip-flops. Variants are therefore identified as a group, and the group is left as soon as one of them matches, marking the remaining ones superseded.

Parameters

db (hawkeye.SBoxDatabase) – The database of known S-boxes.

Returns

The number of identified S-boxes on success, None otherwise.

Return type

int or None

is_round_based(self: hawkeye.CipherCandidate) bool

Check whether the candidate is round-based, i.e., whether its input and output register are the same.

Returns

True if the candidate is round-based, False if it is pipelined.

Return type

bool

locate_sboxes(self: hawkeye.CipherCandidate) Optional[List[hawkeye.SBox]]

Try to locate S-boxes within the round function of the candidate.

Computes an initial set of connected components within the round function. If these components are reasonably small and their input and output sizes match, they are turned into S-boxes right away. Otherwise, iteratively considers more combinational gates starting from the components’ input gates and searches for sub-components.

Returns the S-boxes located by an earlier call unchanged instead of locating them again. Call clear_sboxes to locate them anew.

Returns

The S-boxes of the candidate on success, None otherwise.

Return type

list[hawkeye.SBox] or None

class hawkeye.SBox

An S-box located within the round function of a CipherCandidate.

Owned by the candidate it was located in, which also owns every gate it refers to, so an S-box is only valid for as long as its candidate is.

The exact size and shape of an S-box is not known in advance, so the search deliberately produces more S-boxes than the round function actually contains, among them smaller ones nested inside larger ones. Identification resolves that, see SBoxStatus.

property component

The gates of the connected component that the S-box was located in, including its input flip-flops.

Type

list[hal_py.Gate]

get_combinational_gates(self: hawkeye.SBox) List[hal_py.Gate]

Get the combinational gates computing the outputs of the S-box from its input flip-flops.

Walks back from the output gates within the component and stops at the flip-flops, so the result is the logic of this S-box alone rather than that of the whole component, which several S-boxes may share.

Returns

The combinational gates of the S-box, ordered by gate ID.

Return type

list[hal_py.Gate]

property identified_as

The name of the S-box in the database it was identified as, empty unless status is identified.

Type

str

property input_gates

The input flip-flops of the S-box, ordered by gate ID.

These are the flip-flops of the state register that the S-box reads, and hence the only link between the identified S-box and the state bits it operates on. They are not ordered by S-box input bit: the database matches under affine equivalence, which absorbs any permutation of the input and output bits, so no bit correspondence is established during identification.

Type

list[hal_py.Gate]

property output_gates

The output gates of the S-box, ordered by gate ID. Usually combinational gates feeding the linear layer.

Type

list[hal_py.Gate]

property status

The outcome of trying to identify the S-box, unidentified until identify_sboxes ran.

Type

hawkeye.SBoxStatus

class hawkeye.SBoxStatus

The outcome of trying to identify an S-box, see CipherCandidate.identify_sboxes.

Members:

unidentified : Identification ran but the S-box is not contained in the database.

identified : Identification ran and matched, see SBox.identified_as.

superseded : Identification did not run, another variant of the same S-box was identified before.

property name
class hawkeye.SBoxDatabase

This class holds and manages known S-boxes and allows to perform efficient S-box lookups in the database.

__init__(*args, **kwargs)

Overloaded function.

  1. __init__(self: hawkeye.SBoxDatabase) -> None

    Construct an empty S-box database.

  2. __init__(self: hawkeye.SBoxDatabase, sboxes: Dict[str, List[int]]) -> None

    Construct an S-box database from the given S-boxes.

    param dict[str,list[int]] sboxes

    A dict from S-box name to the respective S-box.

add(*args, **kwargs)

Overloaded function.

  1. add(self: hawkeye.SBoxDatabase, name: str, sbox: List[int]) -> bool

    Add an S-box to the database.

    param str name

    The name of the S-box.

    patam list[int] sbox

    The S-box.

    returns

    True on success, False otherwise.

    rtype

    bool

  2. add(self: hawkeye.SBoxDatabase, sboxes: Dict[str, List[int]]) -> bool

    Add multiple S-boxes to the database.

    param dict[str,list[int]] sboxes

    A dict from S-box name to the respective S-box.

    returns

    True on success, False otherwise.

    rtype

    bool

static compute_linear_representative(sbox: List[int]) List[int]

Compute the linear representative of the given S-box.

Parameters

sbox (list[int]) – The S-box.

Returns

The linear representative.

Return type

list[int]

static from_file(file_path: os.PathLike) Optional[hawkeye.SBoxDatabase]

Construct an S-box database from file.

Parameters

file_path (pathlib.Path) – The path from which to load the S-box database file.

Returns

The S-box database on success, None otherwise.

Return type

hawkeye.SBoxDatabase or None

load(self: hawkeye.SBoxDatabase, file_path: os.PathLike, overwrite: bool = False) bool

Load S-boxes from a file and add them to the existing database.

Parameters
  • file_path (pathlib.Path) – The path from which to load the S-box database file.

  • overwrite (bool) – Set True to overwrite existing database, False otherwise. Defaults to False.

Returns

True on success, False otherwise.

Return type

bool

lookup(self: hawkeye.SBoxDatabase, sbox: List[int]) Optional[str]

Attempt to look up an S-box in the database.

Parameters

sbox (list[int]) – The S-box to look for.

Returns

The S-box name on success, None otherwise.

Return type

str or None

print(self: hawkeye.SBoxDatabase) None

Print the database.

store(self: hawkeye.SBoxDatabase, file_path: os.PathLike) bool

Store the S-box database to a database file.

Parameters

file_path (pathlib.Path) – The path to where to store the S-box database file.

Returns

True on success, False otherwise.

Return type

bool

class hawkeye.HawkeyePlugin

This class provides an interface to integrate the HAWKEYE tool as a plugin within the HAL framework.

property dependencies

A set of plugin names that this plugin depends on.

Type

set[str]

property description

The description of the plugin.

Type

str

get_dependencies(self: hawkeye.HawkeyePlugin) Set[str]

Get a set of plugin names that this plugin depends on.

Returns

A set of plugin names that this plugin depends on.

Return type

set[str]

get_description(self: hawkeye.HawkeyePlugin) str

Get the description of the plugin.

Returns

The description of the plugin.

Return type

str

get_name(self: hawkeye.HawkeyePlugin) str

Get the name of the plugin.

Returns

The name of the plugin.

Return type

str

get_version(self: hawkeye.HawkeyePlugin) str

Get the version of the plugin.

Returns

The version of the plugin.

Return type

str

property name

The name of the plugin.

Type

str

property version

The version of the plugin.

Type

str