samva 2.0.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- samva/__init__.py +15 -0
- samva/__main__.py +7 -0
- samva/cli.py +167 -0
- samva/commands/__init__.py +6 -0
- samva/commands/commands_common.py +107 -0
- samva/commands/commands_evals.py +92 -0
- samva/commands/commands_plot.py +68 -0
- samva/commands/commands_stm32.py +385 -0
- samva/commands/cv32e40p.py +24 -0
- samva/commands/debugger.py +37 -0
- samva/commands/evaluate.py +186 -0
- samva/commands/measure_cycles.py +43 -0
- samva/commands/stm32.py +18 -0
- samva/commands/test_dynprog.py +103 -0
- samva/commands/traitor.py +281 -0
- samva/core/__init__.py +20 -0
- samva/core/common/__init__.py +41 -0
- samva/core/common/analyse_configuration.py +71 -0
- samva/core/common/analysis_utils.py +56 -0
- samva/core/common/attack_path.py +175 -0
- samva/core/common/configuration.py +163 -0
- samva/core/common/instruction_typing.py +149 -0
- samva/core/common/log_utils.py +199 -0
- samva/core/common/timeout.py +28 -0
- samva/core/common/try_import.py +12 -0
- samva/core/fault_positioning/__init__.py +22 -0
- samva/core/fault_positioning/base_pass.py +75 -0
- samva/core/fault_positioning/essential_pass/__init__.py +24 -0
- samva/core/fault_positioning/essential_pass/postvalidation_trim.py +51 -0
- samva/core/fault_positioning/essential_pass/prevalidation_common.py +132 -0
- samva/core/fault_positioning/essential_pass/validation_dynprog.py +1623 -0
- samva/core/fault_positioning/essential_pass/validation_true_nop.py +31 -0
- samva/core/fault_positioning/extra_pass/__init__.py +14 -0
- samva/core/fault_positioning/extra_pass/replay_passes.py +120 -0
- samva/core/fault_positioning/fault_positioner.py +166 -0
- samva/core/model/__init__.py +19 -0
- samva/core/model/db/__init__.py +35 -0
- samva/core/model/db/arch.py +348 -0
- samva/core/model/db/basic_block.py +48 -0
- samva/core/model/db/flow_type.py +37 -0
- samva/core/model/db/function.py +68 -0
- samva/core/model/db/instruction.py +137 -0
- samva/core/model/db/pcode.py +232 -0
- samva/core/model/db/section.py +65 -0
- samva/core/model/db/symbol.py +42 -0
- samva/core/model/essential_pass/__init__.py +23 -0
- samva/core/model/essential_pass/dead_code_elimination.py +51 -0
- samva/core/model/essential_pass/find_first_last_nodes.py +54 -0
- samva/core/model/essential_pass/fix_isolated_nodes.py +51 -0
- samva/core/model/essential_pass/forced_types.py +59 -0
- samva/core/model/essential_pass/prune_cfg.py +47 -0
- samva/core/model/essential_pass/remove_edges_by_type.py +55 -0
- samva/core/model/essential_pass/remove_nodes_by_addr.py +42 -0
- samva/core/model/extra_pass/__init__.py +17 -0
- samva/core/model/extra_pass/aligned_words.py +41 -0
- samva/core/model/extra_pass/convert_type.py +62 -0
- samva/core/model/extra_pass/execute_sp_instruction.py +37 -0
- samva/core/model/extra_pass/replayable_instruction.py +61 -0
- samva/core/model/fault_modeling_pass/__init__.py +12 -0
- samva/core/model/fault_modeling_pass/add_annotation.py +82 -0
- samva/core/model/fault_modeling_pass/add_cost.py +41 -0
- samva/core/model/fault_modeling_pass/skip_jump.py +55 -0
- samva/core/model/ghidra_frontend.py +388 -0
- samva/core/model/samva_model.py +533 -0
- samva/core/model/samva_model_pass_base.py +22 -0
- samva/core/samva.py +868 -0
- samva/core/samva_dataflow.py +853 -0
- samva/core/samva_strategies.py +146 -0
- samva/evaluator/__init__.py +30 -0
- samva/evaluator/evaluator_base.py +119 -0
- samva/evaluator/evaluator_gem5.py +127 -0
- samva/evaluator/evaluator_log_verifypin.py +206 -0
- samva/evaluator/evaluator_openocd.py +134 -0
- samva/evaluator/evaluator_simulation.py +413 -0
- samva/evaluator/evaluator_unicorn.py +231 -0
- samva/evaluator/evaluator_unicorn2.py +562 -0
- samva/experimentation/__init__.py +18 -0
- samva/experimentation/benchmark.py +119 -0
- samva/experimentation/patcher.py +53 -0
- samva/physical/__init__.py +6 -0
- samva/physical/analysis/__init__.py +25 -0
- samva/physical/analysis/campaign.py +199 -0
- samva/physical/analysis/campaign_target.py +11 -0
- samva/physical/analysis/emulator.py +429 -0
- samva/physical/analysis/samva_file_line.py +22 -0
- samva/physical/analysis/simulation_branch_characterisation.py +255 -0
- samva/physical/analysis/simulation_characterisation.py +332 -0
- samva/physical/attacks/__init__.py +14 -0
- samva/physical/attacks/campaign_stm32flash_verifypin.py +105 -0
- samva/physical/boards/__init__.py +14 -0
- samva/physical/boards/base/__init__.py +20 -0
- samva/physical/boards/base/openocd.py +532 -0
- samva/physical/boards/base/serial_communication.py +133 -0
- samva/physical/boards/base/target.py +217 -0
- samva/physical/boards/injectors/__init__.py +16 -0
- samva/physical/boards/injectors/injector.py +30 -0
- samva/physical/boards/injectors/traitor.py +356 -0
- samva/physical/boards/targets/__init__.py +18 -0
- samva/physical/boards/targets/cv32e40p_litex.py +190 -0
- samva/physical/boards/targets/misc/ek-tm4c123gxl.cfg +13 -0
- samva/physical/boards/targets/misc/samva-hs2.cfg +42 -0
- samva/physical/boards/targets/misc/st_nucleo_f3.cfg +10 -0
- samva/physical/boards/targets/stm32.py +119 -0
- samva/physical/boards/targets/stm32flash.py +66 -0
- samva/physical/boards/targets/stm32ram.py +123 -0
- samva/physical/compil/__init__.py +18 -0
- samva/physical/compil/assembler_arm.py +51 -0
- samva/physical/compil/elf_file.py +45 -0
- samva/physical/compil/gcc.py +143 -0
- samva/physical/compil/program_arm.py +183 -0
- samva/plot/__init__.py +21 -0
- samva/plot/faulting_delays.py +35 -0
- samva/plot/histo_amp_delay.py +41 -0
- samva/plot/histo_classification.py +28 -0
- samva/plot/histo_classification2.py +138 -0
- samva/plot/histo_classification3.py +110 -0
- samva/plot/number_difference.py +57 -0
- samva/plot/plot_common.py +29 -0
- samva/plot/test_classification.ipynb +716 -0
- samva-2.0.5.dist-info/METADATA +211 -0
- samva-2.0.5.dist-info/RECORD +124 -0
- samva-2.0.5.dist-info/WHEEL +4 -0
- samva-2.0.5.dist-info/entry_points.txt +3 -0
- samva-2.0.5.dist-info/licenses/LICENSE +661 -0
samva/__init__.py
ADDED
samva/__main__.py
ADDED
samva/cli.py
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# SAMVA, initial software
|
|
2
|
+
# Copyright © INRIA, Affero GPL v3, 2022, v 1.0
|
|
3
|
+
import pyghidra
|
|
4
|
+
import click
|
|
5
|
+
|
|
6
|
+
pyghidra.start()
|
|
7
|
+
|
|
8
|
+
import logging # noqa: E402
|
|
9
|
+
from functools import partial # noqa: E402
|
|
10
|
+
from samva.commands.evaluate import run_evaluate # noqa: E402
|
|
11
|
+
from samva.commands.cv32e40p import run_cv32e40p # noqa: E402
|
|
12
|
+
from samva.commands.debugger import run_debugger # noqa: E402
|
|
13
|
+
from samva.commands.traitor import run_traitor, run_traitor_characterization # noqa: E402
|
|
14
|
+
from samva.commands.stm32 import run_stm32 # noqa: E402
|
|
15
|
+
from samva.commands.measure_cycles import run_measure_cycles # noqa: E402
|
|
16
|
+
from samva.commands.test_dynprog import run_test_dynprog # noqa: E402
|
|
17
|
+
|
|
18
|
+
log = logging.getLogger(name=f"samva-{__name__}")
|
|
19
|
+
log.setLevel(logging.DEBUG)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# @click.group()
|
|
23
|
+
@click.group(chain=True)
|
|
24
|
+
def main():
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
@main.command()
|
|
28
|
+
# Mandatory arguments
|
|
29
|
+
@click.argument("bin_path", required=True, type=click.Path(exists=True, file_okay=True, writable=False))
|
|
30
|
+
@click.argument("samva_config_path", required=True, type=click.Path(exists=True, file_okay=True, writable=False))
|
|
31
|
+
# Most common options, comes with reasonable default values
|
|
32
|
+
@click.option("--result_folder", required=False, type=click.Path(exists=True, file_okay=False, writable=True), help="Directory where the results are stored.")
|
|
33
|
+
@click.option("--fault_model", required=False, type=click.Choice(["TrueNOP", "Replay"], case_sensitive=False), default="TrueNOP")
|
|
34
|
+
@click.option("--fault_parameters", required=False, type=(int, int, int), default=(1, 10, 1), help="Specify the fault model as argument if not in configuration file.")
|
|
35
|
+
# Options related to new algorithms
|
|
36
|
+
@click.option("--max_paths_per_segment", required=False, type=int, default=None, help="Maximum number of paths to find for each attack segment.")
|
|
37
|
+
@click.option("--max_cost_per_segment", required=False, type=int, default=None, help="Maximum number of faults allowed in each attack segment.")
|
|
38
|
+
@click.option("--max_cost_for_simulation", required=False, type=int, default=None, help="Maximum number of faults considered for simulation.")
|
|
39
|
+
@click.option("--max_attack_paths", required=False, type=int, default=None, help="Maximum number of attack paths to find.")
|
|
40
|
+
# Options related to attack paths evaluation
|
|
41
|
+
@click.option("--evaluator", type=click.Choice(["unicorn", "gem5", "none"], case_sensitive=False), default="unicorn", help="Evaluator used to validate the attack paths.")
|
|
42
|
+
@click.option("--unicorn_full_init/--no-unicorn_full_init", default=True, help="Specific to unicorn emulation. PC is set to first attacked address, without running the full program init to avoid impossible loop due hardware interactions.")
|
|
43
|
+
@click.option("--log_variable", "-lv", multiple=True, help="Program symbols that will be logged during evaluation.")
|
|
44
|
+
# Options enabling early exit
|
|
45
|
+
@click.option("--timeout", required=False, type=float, default=-1.0, help="Analysis timeout.")
|
|
46
|
+
@click.option("--stop_at_robustness/--no_stop_at_robustness", default=True, help="Stop after finding the robustness factor.")
|
|
47
|
+
# Options that enable the previous algorithms, for compatibility purposes
|
|
48
|
+
@click.option("--deprecated/--no_deprecated", required=False, type=bool, default=False, help="Enable previous algorithms.")
|
|
49
|
+
@click.option("--deprecated_exhaustive/--no-deprecated_exhaustive", required=False, type=bool, default=False, help="Enable exhaustive search for backtracking fault positioning.")
|
|
50
|
+
@click.option("--deprecated_max_solutions", required=False, type=int, default=5, help="Maximum number of solutions found per attack path.")
|
|
51
|
+
# Verbose mode, useful for debug.
|
|
52
|
+
@click.option("--verbose/--no-verbose", default=False, help="Displays information during the analysis.")
|
|
53
|
+
def evaluate(bin_path, samva_config_path, result_folder, fault_model,
|
|
54
|
+
fault_parameters, max_paths_per_segment, max_cost_per_segment, max_cost_for_simulation,
|
|
55
|
+
max_attack_paths, evaluator, unicorn_full_init, log_variable, timeout, stop_at_robustness,
|
|
56
|
+
deprecated, deprecated_exhaustive, deprecated_max_solutions, verbose):
|
|
57
|
+
return partial(run_evaluate, bin_path=bin_path, samva_config_path=samva_config_path, result_folder=result_folder,
|
|
58
|
+
fault_model=fault_model, fault_parameters=fault_parameters,
|
|
59
|
+
max_paths_per_segment=max_paths_per_segment, max_cost_per_segment=max_cost_per_segment,
|
|
60
|
+
max_cost_for_simulation=max_cost_for_simulation, max_attack_paths=max_attack_paths,
|
|
61
|
+
evaluator=evaluator, unicorn_full_init=unicorn_full_init, log_variable=log_variable,
|
|
62
|
+
timeout=timeout, stop_at_robustness=stop_at_robustness, deprecated=deprecated,
|
|
63
|
+
deprecated_exhaustive=deprecated_exhaustive, deprecated_max_solutions=deprecated_max_solutions,
|
|
64
|
+
verbose=verbose)
|
|
65
|
+
|
|
66
|
+
@main.command()
|
|
67
|
+
@click.argument("port", required=True)
|
|
68
|
+
@click.option("--verbose/--no-verbose", default=False, help="Displays information during the analysis.")
|
|
69
|
+
def cv32e40p(port, verbose):
|
|
70
|
+
return partial(run_cv32e40p, port=port, verbose=verbose)
|
|
71
|
+
|
|
72
|
+
@main.command()
|
|
73
|
+
@click.option("--verbose/--no-verbose", default=False, help="Displays information during the analysis.")
|
|
74
|
+
def debugger(verbose):
|
|
75
|
+
return partial(run_debugger, verbose=verbose)
|
|
76
|
+
|
|
77
|
+
@main.command()
|
|
78
|
+
@click.argument("port", required=True)
|
|
79
|
+
@click.option("--traitor_amplitude", "-ta", type=int, multiple=True, help="Traitor amplitude used during the attacks.")
|
|
80
|
+
@click.option("--traitor_fault", "-tf", type=(int, int), multiple=True, help="Traitor fault <delay, duration> parameter.")
|
|
81
|
+
@click.option("--reset/--no-reset", default=False, help="Reset Traitor configuration.")
|
|
82
|
+
@click.option("--repetitions", required=False, type=int, default=5, help="Number of attack per configuration tested.")
|
|
83
|
+
@click.option("--campaign/--no-campaign", default=False, help="Launch a Traitor campaign.")
|
|
84
|
+
@click.option("--verbose/--no-verbose", default=False, help="Displays information during the analysis.")
|
|
85
|
+
def traitor(port, traitor_amplitude, traitor_fault, reset, repetitions, campaign, verbose):
|
|
86
|
+
return partial(run_traitor, port=port, amplitudes=traitor_amplitude, temporal_parameters=traitor_fault, reset=reset,
|
|
87
|
+
repetitions=repetitions, campaign=campaign, verbose=verbose)
|
|
88
|
+
|
|
89
|
+
@main.command()
|
|
90
|
+
@click.option('--custom_port', default=None, type=str)
|
|
91
|
+
@click.option('--stlink', default=None, type=str)
|
|
92
|
+
@click.option('--program', default=None, type=str)
|
|
93
|
+
@click.option("--verbose/--no-verbose", default=False, help="Displays information during the analysis.")
|
|
94
|
+
def stm32(custom_port, stlink, program, verbose):
|
|
95
|
+
return partial(run_stm32, custom_port=custom_port, stlink=stlink, program=program, verbose=verbose)
|
|
96
|
+
|
|
97
|
+
@main.command()
|
|
98
|
+
@click.argument("addr1", type=str)
|
|
99
|
+
@click.argument("addr2", type=str)
|
|
100
|
+
@click.option("--verbose/--no-verbose", default=False, help="Displays information during the analysis.")
|
|
101
|
+
def measure_cycles(addr1, addr2, verbose):
|
|
102
|
+
return partial(run_measure_cycles, addr1=addr1, addr2=addr2, verbose=verbose)
|
|
103
|
+
|
|
104
|
+
@main.command()
|
|
105
|
+
@click.option('--trace', type=str, help="Complete trace.")
|
|
106
|
+
@click.option('--seed', type=int, help="Random seed; if 0, unseeded.", default=0)
|
|
107
|
+
@click.option('--length', type=int, help="Length of the generated trace.")
|
|
108
|
+
@click.option('--repeats', type=int, help="Number of traces to generate.")
|
|
109
|
+
@click.option('--fault_parameters', type=(int, int, int), help="Specify the fault model as argument if not in "
|
|
110
|
+
"configuration file.")
|
|
111
|
+
@click.option('--max_solutions', required=False, type=int, default=100)
|
|
112
|
+
@click.option('--exhaustive/--no-exhaustive', required=False, type=bool, default=False)
|
|
113
|
+
@click.option('--verbose/--no-verbose', default=False, help="Displays information during the core.")
|
|
114
|
+
def test_dynprog(trace, seed, length, repeats, fault_parameters, max_solutions, exhaustive, verbose):
|
|
115
|
+
return partial(run_test_dynprog, trace=trace, seed=seed, length=length, repeats=repeats,
|
|
116
|
+
fault_parameters=fault_parameters, max_solutions=max_solutions, exhaustive=exhaustive,
|
|
117
|
+
verbose=verbose)
|
|
118
|
+
|
|
119
|
+
@main.command()
|
|
120
|
+
@click.argument("port", required=True)
|
|
121
|
+
@click.option('--amplitude_range', type=(int, int), required=True)
|
|
122
|
+
@click.option('--delay_range', type=(int, int), required=True)
|
|
123
|
+
@click.option('--duration_range', type=(int, int), required=True)
|
|
124
|
+
@click.option("--repetitions", required=False, type=int, default=50, help="Number of attack per configuration tested.")
|
|
125
|
+
@click.option("--amplitude_step", required=False, type=int, default=1)
|
|
126
|
+
@click.option("--delay_step", required=False, type=int, default=1)
|
|
127
|
+
@click.option("--duration_step", required=False, type=int, default=1)
|
|
128
|
+
@click.option('--verbose/--no-verbose', default=False, help="Displays information during the core.")
|
|
129
|
+
def traitor_characterization(port, amplitude_range, delay_range, duration_range, repetitions, amplitude_step,
|
|
130
|
+
delay_step, duration_step, verbose):
|
|
131
|
+
return partial (run_traitor_characterization, port=port, amplitude_range=amplitude_range, delay_range=delay_range,
|
|
132
|
+
duration_range=duration_range, repetitions=repetitions, amplitude_step=amplitude_step,
|
|
133
|
+
delay_step=delay_step, duration_step=duration_step, verbose=verbose)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# TODO: must integrate the other commands with the new chaining command system
|
|
137
|
+
# # STM32 commands
|
|
138
|
+
# main.add_command(stm32.simulate, "simulate")
|
|
139
|
+
#
|
|
140
|
+
# # Plotting commands
|
|
141
|
+
# main.add_command(plot.plot_faulting_delays, "plot_faulting_delays") # amplitude = f(burst_size)
|
|
142
|
+
# main.add_command(plot.plot_amp_delay, "plot_amp_delay") # amplitude = f(burst_size)
|
|
143
|
+
# main.add_command(plot.plot_histo_classification, "plot_histo_classification") # fault classification for each delay
|
|
144
|
+
# main.add_command(plot.plot_histo_classification2, "plot_histo_classification2") # for each delay histo(amplitude)
|
|
145
|
+
# main.add_command(plot.plot_number_difference, "plot_number_difference") # 3D plot amplitude = f(duration, delay)
|
|
146
|
+
#
|
|
147
|
+
# # Evaluation commands for SAMVA v2
|
|
148
|
+
# main.add_command(evals.eval_verifypin_truenop, "eval_verifypin_truenop")
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@main.result_callback()
|
|
152
|
+
def process_pipeline(processors):
|
|
153
|
+
value = None
|
|
154
|
+
for processor in processors:
|
|
155
|
+
value = processor(value)
|
|
156
|
+
|
|
157
|
+
if value is not None and type(value) is dict:
|
|
158
|
+
for binary_name in value.keys():
|
|
159
|
+
if "log" in value[binary_name] and len(value[binary_name]["log"]) > 0:
|
|
160
|
+
log.info("")
|
|
161
|
+
log.info("")
|
|
162
|
+
log.info("--- SAMVA Log Summary ---")
|
|
163
|
+
for line in value[binary_name]["log"]:
|
|
164
|
+
log.info(line)
|
|
165
|
+
|
|
166
|
+
if __name__ == '__main__':
|
|
167
|
+
main()
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# SAMVA, initial software
|
|
2
|
+
# Copyright © INRIA, Affero GPL v3, 2022, v 1.0
|
|
3
|
+
|
|
4
|
+
import ast
|
|
5
|
+
import logging
|
|
6
|
+
from functools import wraps
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
|
|
10
|
+
from samva.core.common import setup_logging
|
|
11
|
+
from samva.core.common.analysis_utils import Timing
|
|
12
|
+
|
|
13
|
+
log = logging.getLogger(name=f"samva-{__name__}")
|
|
14
|
+
log.setLevel(logging.DEBUG)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BasedIntParamType(click.ParamType):
|
|
18
|
+
name = "integer"
|
|
19
|
+
|
|
20
|
+
def convert(self, value, param, ctx):
|
|
21
|
+
if isinstance(value, int):
|
|
22
|
+
return value
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
if value[:2].lower() == "0x":
|
|
26
|
+
return int(value[2:], 16)
|
|
27
|
+
elif value[:1] == "0":
|
|
28
|
+
return int(value, 8)
|
|
29
|
+
return int(value, 10)
|
|
30
|
+
except ValueError:
|
|
31
|
+
self.fail(f"{value!r} is not a valid integer", param, ctx)
|
|
32
|
+
|
|
33
|
+
BASED_INT = BasedIntParamType()
|
|
34
|
+
|
|
35
|
+
class PythonLiteralOption(click.Option):
|
|
36
|
+
|
|
37
|
+
def type_cast_value(self, ctx, value):
|
|
38
|
+
if value is None:
|
|
39
|
+
return None
|
|
40
|
+
try:
|
|
41
|
+
return ast.literal_eval(value)
|
|
42
|
+
except:
|
|
43
|
+
raise click.BadParameter(value)
|
|
44
|
+
|
|
45
|
+
# Decorators
|
|
46
|
+
|
|
47
|
+
def check_logging(command):
|
|
48
|
+
@wraps(command)
|
|
49
|
+
def wrapper(*args, **kwargs):
|
|
50
|
+
verbose = kwargs.get("verbose", False)
|
|
51
|
+
setup_logging(logging.DEBUG if verbose else logging.INFO)
|
|
52
|
+
return command(*args, **kwargs)
|
|
53
|
+
return wrapper
|
|
54
|
+
|
|
55
|
+
def must_be_chained(required: bool):
|
|
56
|
+
def decorator(command):
|
|
57
|
+
@wraps(command)
|
|
58
|
+
def wrapper(previous, *args, **kwargs):
|
|
59
|
+
if required and previous is None:
|
|
60
|
+
command_name = command.__name__
|
|
61
|
+
if command_name.startswith("run_"):
|
|
62
|
+
command_name = command_name[4:]
|
|
63
|
+
raise RuntimeError(f"\"{command_name}\" command must be chained after another command.")
|
|
64
|
+
return command(previous if previous is not None else {}, *args, **kwargs)
|
|
65
|
+
return wrapper
|
|
66
|
+
return decorator
|
|
67
|
+
|
|
68
|
+
def hardware_attack(command):
|
|
69
|
+
@wraps(command)
|
|
70
|
+
def wrapper(previous, *args, **kwargs):
|
|
71
|
+
for binary_name, result in previous.items():
|
|
72
|
+
create_target = result.get("create_target")
|
|
73
|
+
if create_target is None:
|
|
74
|
+
log.error(f"No hardware target for {binary_name}")
|
|
75
|
+
continue
|
|
76
|
+
|
|
77
|
+
target = create_target()
|
|
78
|
+
|
|
79
|
+
with Timing() as t_attack:
|
|
80
|
+
attack_tested, attack_validated = command(
|
|
81
|
+
previous,
|
|
82
|
+
target=target,
|
|
83
|
+
binary_name=binary_name,
|
|
84
|
+
*args,
|
|
85
|
+
**kwargs,
|
|
86
|
+
)
|
|
87
|
+
duration = t_attack.time
|
|
88
|
+
|
|
89
|
+
result["hardware_result"] = {
|
|
90
|
+
"attack_path_tested": attack_tested,
|
|
91
|
+
"attack_path_validated": attack_validated,
|
|
92
|
+
"duration_sec": duration,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if attack_tested == 0:
|
|
96
|
+
txt = "No attack path tested due to previous error."
|
|
97
|
+
else:
|
|
98
|
+
txt = (
|
|
99
|
+
f"Hardware validation - Validated {attack_validated} / "
|
|
100
|
+
f"{attack_tested} in {duration:.2f} seconds "
|
|
101
|
+
f"(mean of {duration / attack_tested:.2f} secs)."
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
log.info(txt)
|
|
105
|
+
result["log"].append(txt)
|
|
106
|
+
return previous
|
|
107
|
+
return wrapper
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import click
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from samva.experimentation import Benchmark, BenchmarkIterator
|
|
6
|
+
from samva.core import Samva
|
|
7
|
+
from samva.evaluator import EvaluatorBase
|
|
8
|
+
from samva.evaluator.evaluator_unicorn2 import EvaluatorSimulationUnicorn2
|
|
9
|
+
from samva.core.common.log_utils import setup_logging
|
|
10
|
+
from samva.core.common.attack_path import FaultParameters, FaultModel
|
|
11
|
+
from samva.core.common.analysis_utils import Timing
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
log = logging.getLogger(name=f"samva-{__name__}")
|
|
15
|
+
log.setLevel(logging.DEBUG)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# @click.option('--fault_parameters', type=(int, int, int))
|
|
19
|
+
# @click.option('--fault_model', type=click.Choice(['TrueNOP', 'Replay'], case_sensitive=False), default="TrueNOP")
|
|
20
|
+
|
|
21
|
+
def path_verifyPIN(benchmark_folder, n, flags=""):
|
|
22
|
+
flags = "_" + flags if flags else ""
|
|
23
|
+
elf = os.path.join(benchmark_folder, f"build/verifypin_v{n}_cv32e40p/verifypin_v{n}_cv32e40p{flags}.elf")
|
|
24
|
+
config = os.path.join(benchmark_folder, f"samva_files/verifypin_v{n}_cv32e40p.json")
|
|
25
|
+
return elf, config
|
|
26
|
+
|
|
27
|
+
@click.command()
|
|
28
|
+
@click.option("--benchmark_folder", default="../samva_benchmarks")
|
|
29
|
+
@click.option('--verbose/--no-verbose', default=False, help="Displays information during the core.")
|
|
30
|
+
def eval_verifypin_truenop(benchmark_folder, verbose):
|
|
31
|
+
setup_logging(logging_level=logging.DEBUG if verbose else logging.INFO)
|
|
32
|
+
|
|
33
|
+
# Fixed settings
|
|
34
|
+
fault_model = FaultModel.TrueNOP
|
|
35
|
+
strategy = "default"
|
|
36
|
+
# Won't apply to the new algorithms
|
|
37
|
+
exhaustive = False
|
|
38
|
+
fake_timeout = 999999
|
|
39
|
+
|
|
40
|
+
samvas = dict()
|
|
41
|
+
evaluators = dict()
|
|
42
|
+
|
|
43
|
+
verifypin_versions = [0, 1, 2, 3, 4, 5, 6, 7]
|
|
44
|
+
# verifypin_versions = [5]
|
|
45
|
+
|
|
46
|
+
#=== Load programs ===#
|
|
47
|
+
|
|
48
|
+
for n in verifypin_versions:
|
|
49
|
+
path_elf, path_config = path_verifyPIN(benchmark_folder, n)
|
|
50
|
+
|
|
51
|
+
samvas[n] = Samva(path_elf, config_file=path_config, fault_model=fault_model, strategy=strategy,
|
|
52
|
+
exhaustive=exhaustive)
|
|
53
|
+
evaluators[n] = EvaluatorSimulationUnicorn2(samvas[n].samva_model, fault_model)
|
|
54
|
+
|
|
55
|
+
#=== Run tests ===#
|
|
56
|
+
|
|
57
|
+
def attacks_within_time(n, timeout: float) -> int:
|
|
58
|
+
fault_parameters = FaultParameters(1, 4, 1, fake_timeout)
|
|
59
|
+
attack_result = samvas[n].find_data_aware_attack_paths(fault_parameters, evaluators[n],
|
|
60
|
+
max_paths_per_segment=None, max_cost_per_segment=None, max_cost_for_simulation=None,
|
|
61
|
+
stop_when_robustness_found=False, timeout=timeout)
|
|
62
|
+
attacks_found = len(attack_result.attack_paths or [])
|
|
63
|
+
print(f"Listed {attacks_found} attacks in {timeout} seconds")
|
|
64
|
+
return attacks_found
|
|
65
|
+
|
|
66
|
+
def robustness_map(fp, ns, low_width: int, high_width: int, min_distance: int, max_budget: int, timeout: floats) \
|
|
67
|
+
-> list[list[int]]:
|
|
68
|
+
fp.write(f"version,min_width,max_width,robustness,analysis_time\n")
|
|
69
|
+
fp.flush()
|
|
70
|
+
for n in ns:
|
|
71
|
+
for max_width in range(low_width, high_width + 1):
|
|
72
|
+
for min_width in range(low_width, max_width + 1):
|
|
73
|
+
fault_parameters = FaultParameters(min_width, max_width, min_distance, fake_timeout)
|
|
74
|
+
print(f"verifyPIN_v{n}: ({min_width}, {max_width}, {min_distance})...")
|
|
75
|
+
with Timing() as t_analysis:
|
|
76
|
+
attack_result = samvas[n].find_data_aware_attack_paths(fault_parameters, evaluators[n],
|
|
77
|
+
max_paths_per_segment=None, max_cost_per_segment=max_budget, max_cost_for_simulation=None,
|
|
78
|
+
stop_when_robustness_found=True, timeout=timeout)
|
|
79
|
+
|
|
80
|
+
if attack_result.robustness is not None:
|
|
81
|
+
robustness = str(attack_result.robustness)
|
|
82
|
+
elif samvas[n].bound_min_attacks is None:
|
|
83
|
+
robustness = "+inf"
|
|
84
|
+
else:
|
|
85
|
+
robustness = f">= {samvas[n].bound_min_attacks - 1}"
|
|
86
|
+
|
|
87
|
+
print(f"verifyPIN_v{n}: ({min_width}, {max_width}, {min_distance}) -> {robustness} in {t_analysis}")
|
|
88
|
+
fp.write(f"{n},{min_width},{max_width},{robustness},{t_analysis.time}\n")
|
|
89
|
+
fp.flush()
|
|
90
|
+
|
|
91
|
+
with open("verifypin_truenop_robustness.csv", "w") as fp:
|
|
92
|
+
robustness_map(fp, verifypin_versions, 1, 10, 1, max_budget=None, timeout=30.0)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# SAMVA, initial software
|
|
2
|
+
# Copyright © INRIA, Affero GPL v3, 2022, v 1.0
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
|
|
8
|
+
@click.command()
|
|
9
|
+
@click.argument('input_file', required=1, type=str)
|
|
10
|
+
@click.argument('output_file', required=0, type=str, default=None)
|
|
11
|
+
@click.option('--tex', default=False)
|
|
12
|
+
def plot_faulting_delays(input_file, output_file, tex):
|
|
13
|
+
from samva.plot.faulting_delays import faulting_delays
|
|
14
|
+
if output_file is None:
|
|
15
|
+
path_split = os.path.splitext(input_file)
|
|
16
|
+
output_file = path_split[0] + "_delays.pdf"
|
|
17
|
+
faulting_delays(input_file, output_file, tex)
|
|
18
|
+
|
|
19
|
+
@click.command()
|
|
20
|
+
@click.argument('input_file', required=1, type=str)
|
|
21
|
+
@click.argument('output_file', required=0, type=str, default=None)
|
|
22
|
+
@click.argument('fault', required=1, type=str)
|
|
23
|
+
@click.option('--tex', default=False)
|
|
24
|
+
def plot_amp_delay(input_file, output_file, fault, tex):
|
|
25
|
+
from samva.plot.histo_amp_delay import faulting_ampdelay
|
|
26
|
+
faulting_ampdelay(input_file, output_file, fault, tex)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@click.command()
|
|
30
|
+
@click.argument('input_file', required=1, type=str)
|
|
31
|
+
@click.argument('output_file', required=0, type=str, default=None)
|
|
32
|
+
@click.argument('delay_value', required=1, type=int)
|
|
33
|
+
@click.option('--tex', default=False)
|
|
34
|
+
def plot_histo_classification(input_file, output_file, delay_value, tex):
|
|
35
|
+
if output_file is None:
|
|
36
|
+
path_split = os.path.splitext(input_file)
|
|
37
|
+
output_file = path_split[0] + "_hist.pdf"
|
|
38
|
+
from samva.plot.histo_classification import histo_classification
|
|
39
|
+
histo_classification(input_file, output_file, delay_value, tex)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@click.command()
|
|
43
|
+
@click.argument('input_file', required=1, type=str)
|
|
44
|
+
@click.argument('output_file', required=0, type=str, default=None)
|
|
45
|
+
@click.option('--others_perc', type=int, default=5)
|
|
46
|
+
@click.option('--percentage', default=False)
|
|
47
|
+
@click.option('--duration_group', default=False)
|
|
48
|
+
@click.option('--duration_group_value', type=int, default=4)
|
|
49
|
+
@click.option('--tex', default=False)
|
|
50
|
+
def plot_histo_classification2(input_file, output_file, others_perc, percentage, duration_group,
|
|
51
|
+
duration_group_value, tex):
|
|
52
|
+
if output_file is None:
|
|
53
|
+
path_split = os.path.splitext(input_file)
|
|
54
|
+
output_file = path_split[0] + "_hist2.pdf"
|
|
55
|
+
from samva.plot.histo_classification2 import histo_classification2
|
|
56
|
+
histo_classification2(input_file, output_file, others_perc, percentage, duration_group, duration_group_value, tex)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@click.command()
|
|
60
|
+
@click.argument('input_file', required=1, type=str)
|
|
61
|
+
@click.argument('output_file', required=0, type=str, default=None)
|
|
62
|
+
@click.option('--tex', default=False)
|
|
63
|
+
def plot_number_difference(input_file, output_file, tex):
|
|
64
|
+
if output_file is None:
|
|
65
|
+
path_split = os.path.splitext(input_file)
|
|
66
|
+
output_file = path_split[0] + "_numbdiff.pdf"
|
|
67
|
+
from samva.plot.number_difference import number_difference
|
|
68
|
+
number_difference(input_file, output_file, tex)
|