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.
Files changed (124) hide show
  1. samva/__init__.py +15 -0
  2. samva/__main__.py +7 -0
  3. samva/cli.py +167 -0
  4. samva/commands/__init__.py +6 -0
  5. samva/commands/commands_common.py +107 -0
  6. samva/commands/commands_evals.py +92 -0
  7. samva/commands/commands_plot.py +68 -0
  8. samva/commands/commands_stm32.py +385 -0
  9. samva/commands/cv32e40p.py +24 -0
  10. samva/commands/debugger.py +37 -0
  11. samva/commands/evaluate.py +186 -0
  12. samva/commands/measure_cycles.py +43 -0
  13. samva/commands/stm32.py +18 -0
  14. samva/commands/test_dynprog.py +103 -0
  15. samva/commands/traitor.py +281 -0
  16. samva/core/__init__.py +20 -0
  17. samva/core/common/__init__.py +41 -0
  18. samva/core/common/analyse_configuration.py +71 -0
  19. samva/core/common/analysis_utils.py +56 -0
  20. samva/core/common/attack_path.py +175 -0
  21. samva/core/common/configuration.py +163 -0
  22. samva/core/common/instruction_typing.py +149 -0
  23. samva/core/common/log_utils.py +199 -0
  24. samva/core/common/timeout.py +28 -0
  25. samva/core/common/try_import.py +12 -0
  26. samva/core/fault_positioning/__init__.py +22 -0
  27. samva/core/fault_positioning/base_pass.py +75 -0
  28. samva/core/fault_positioning/essential_pass/__init__.py +24 -0
  29. samva/core/fault_positioning/essential_pass/postvalidation_trim.py +51 -0
  30. samva/core/fault_positioning/essential_pass/prevalidation_common.py +132 -0
  31. samva/core/fault_positioning/essential_pass/validation_dynprog.py +1623 -0
  32. samva/core/fault_positioning/essential_pass/validation_true_nop.py +31 -0
  33. samva/core/fault_positioning/extra_pass/__init__.py +14 -0
  34. samva/core/fault_positioning/extra_pass/replay_passes.py +120 -0
  35. samva/core/fault_positioning/fault_positioner.py +166 -0
  36. samva/core/model/__init__.py +19 -0
  37. samva/core/model/db/__init__.py +35 -0
  38. samva/core/model/db/arch.py +348 -0
  39. samva/core/model/db/basic_block.py +48 -0
  40. samva/core/model/db/flow_type.py +37 -0
  41. samva/core/model/db/function.py +68 -0
  42. samva/core/model/db/instruction.py +137 -0
  43. samva/core/model/db/pcode.py +232 -0
  44. samva/core/model/db/section.py +65 -0
  45. samva/core/model/db/symbol.py +42 -0
  46. samva/core/model/essential_pass/__init__.py +23 -0
  47. samva/core/model/essential_pass/dead_code_elimination.py +51 -0
  48. samva/core/model/essential_pass/find_first_last_nodes.py +54 -0
  49. samva/core/model/essential_pass/fix_isolated_nodes.py +51 -0
  50. samva/core/model/essential_pass/forced_types.py +59 -0
  51. samva/core/model/essential_pass/prune_cfg.py +47 -0
  52. samva/core/model/essential_pass/remove_edges_by_type.py +55 -0
  53. samva/core/model/essential_pass/remove_nodes_by_addr.py +42 -0
  54. samva/core/model/extra_pass/__init__.py +17 -0
  55. samva/core/model/extra_pass/aligned_words.py +41 -0
  56. samva/core/model/extra_pass/convert_type.py +62 -0
  57. samva/core/model/extra_pass/execute_sp_instruction.py +37 -0
  58. samva/core/model/extra_pass/replayable_instruction.py +61 -0
  59. samva/core/model/fault_modeling_pass/__init__.py +12 -0
  60. samva/core/model/fault_modeling_pass/add_annotation.py +82 -0
  61. samva/core/model/fault_modeling_pass/add_cost.py +41 -0
  62. samva/core/model/fault_modeling_pass/skip_jump.py +55 -0
  63. samva/core/model/ghidra_frontend.py +388 -0
  64. samva/core/model/samva_model.py +533 -0
  65. samva/core/model/samva_model_pass_base.py +22 -0
  66. samva/core/samva.py +868 -0
  67. samva/core/samva_dataflow.py +853 -0
  68. samva/core/samva_strategies.py +146 -0
  69. samva/evaluator/__init__.py +30 -0
  70. samva/evaluator/evaluator_base.py +119 -0
  71. samva/evaluator/evaluator_gem5.py +127 -0
  72. samva/evaluator/evaluator_log_verifypin.py +206 -0
  73. samva/evaluator/evaluator_openocd.py +134 -0
  74. samva/evaluator/evaluator_simulation.py +413 -0
  75. samva/evaluator/evaluator_unicorn.py +231 -0
  76. samva/evaluator/evaluator_unicorn2.py +562 -0
  77. samva/experimentation/__init__.py +18 -0
  78. samva/experimentation/benchmark.py +119 -0
  79. samva/experimentation/patcher.py +53 -0
  80. samva/physical/__init__.py +6 -0
  81. samva/physical/analysis/__init__.py +25 -0
  82. samva/physical/analysis/campaign.py +199 -0
  83. samva/physical/analysis/campaign_target.py +11 -0
  84. samva/physical/analysis/emulator.py +429 -0
  85. samva/physical/analysis/samva_file_line.py +22 -0
  86. samva/physical/analysis/simulation_branch_characterisation.py +255 -0
  87. samva/physical/analysis/simulation_characterisation.py +332 -0
  88. samva/physical/attacks/__init__.py +14 -0
  89. samva/physical/attacks/campaign_stm32flash_verifypin.py +105 -0
  90. samva/physical/boards/__init__.py +14 -0
  91. samva/physical/boards/base/__init__.py +20 -0
  92. samva/physical/boards/base/openocd.py +532 -0
  93. samva/physical/boards/base/serial_communication.py +133 -0
  94. samva/physical/boards/base/target.py +217 -0
  95. samva/physical/boards/injectors/__init__.py +16 -0
  96. samva/physical/boards/injectors/injector.py +30 -0
  97. samva/physical/boards/injectors/traitor.py +356 -0
  98. samva/physical/boards/targets/__init__.py +18 -0
  99. samva/physical/boards/targets/cv32e40p_litex.py +190 -0
  100. samva/physical/boards/targets/misc/ek-tm4c123gxl.cfg +13 -0
  101. samva/physical/boards/targets/misc/samva-hs2.cfg +42 -0
  102. samva/physical/boards/targets/misc/st_nucleo_f3.cfg +10 -0
  103. samva/physical/boards/targets/stm32.py +119 -0
  104. samva/physical/boards/targets/stm32flash.py +66 -0
  105. samva/physical/boards/targets/stm32ram.py +123 -0
  106. samva/physical/compil/__init__.py +18 -0
  107. samva/physical/compil/assembler_arm.py +51 -0
  108. samva/physical/compil/elf_file.py +45 -0
  109. samva/physical/compil/gcc.py +143 -0
  110. samva/physical/compil/program_arm.py +183 -0
  111. samva/plot/__init__.py +21 -0
  112. samva/plot/faulting_delays.py +35 -0
  113. samva/plot/histo_amp_delay.py +41 -0
  114. samva/plot/histo_classification.py +28 -0
  115. samva/plot/histo_classification2.py +138 -0
  116. samva/plot/histo_classification3.py +110 -0
  117. samva/plot/number_difference.py +57 -0
  118. samva/plot/plot_common.py +29 -0
  119. samva/plot/test_classification.ipynb +716 -0
  120. samva-2.0.5.dist-info/METADATA +211 -0
  121. samva-2.0.5.dist-info/RECORD +124 -0
  122. samva-2.0.5.dist-info/WHEEL +4 -0
  123. samva-2.0.5.dist-info/entry_points.txt +3 -0
  124. samva-2.0.5.dist-info/licenses/LICENSE +661 -0
@@ -0,0 +1,385 @@
1
+ # # SAMVA, initial software
2
+ # # Copyright © INRIA, Affero GPL v3, 2022, v 1.0
3
+ #
4
+ # import csv
5
+ # import json
6
+ # import logging
7
+ # import os
8
+ # import sys
9
+ # import time
10
+ # from pathlib import Path
11
+ #
12
+ # import click
13
+ # from tqdm import tqdm
14
+ # from tqdm.contrib.logging import logging_redirect_tqdm
15
+ #
16
+ # from samva.physical.analysis.samva_file_line import convert_attack_path
17
+ # from samva.physical.boards.targets.cv32e40p_litex import CV32E40P_Litex
18
+ # from samva.core.common import setup_logging
19
+ # from samva.core import Samva
20
+ # from samva.core.model.db import Instruction
21
+ # from samva.commands.commands_common import PythonLiteralOption, BASED_INT
22
+ # from samva.core.common.attack_path import FaultModel, FaultParameters
23
+ # from samva.experimentation.patcher import Patcher
24
+ # from samva.physical.analysis.campaign import CampaignTarget
25
+ # from samva.physical.analysis.simulation_branch_characterisation import SimulationBranchCharacterisation
26
+ # from samva.physical.analysis.simulation_characterisation import SimulationCharacterisation, default_register_value, \
27
+ # dict_register_value
28
+ # from samva.physical.attacks.campaign_stm32flash_verifypin import Campaign_STM32Flash_Verifypin
29
+ # from samva.physical.boards.targets.stm32flash import STM32Flash
30
+ # from samva.physical.boards.injectors.traitor import Traitor
31
+ #
32
+ # log = logging.getLogger(name=f"samva-{__name__}")
33
+ # log.setLevel(logging.DEBUG)
34
+ #
35
+ # samva_config_characterization = """
36
+ # {
37
+ # "starting_point": "characterization",
38
+ # "attack_points": [
39
+ # {
40
+ # "target": "instruction",
41
+ # "address": "characterization"
42
+ # }
43
+ # ],
44
+ # "global_avoid": [],
45
+ # "forced_types": []
46
+ # }
47
+ # """
48
+ #
49
+ # @click.command()
50
+ # @click.argument('samva_results_dir', required=1, type=click.Path(exists=True, file_okay=False, writable=True))
51
+ # @click.argument('verifypin_dir', required=1, type=click.Path(exists=True, file_okay=False))
52
+ # @click.argument('traitor_port', required=1, type=str)
53
+ # # @click.argument('stm32_port', required=1, type=str)
54
+ # @click.option('--attacked_versions', cls=PythonLiteralOption, default=None,
55
+ # help="Limit the attack to selected verifypin versions.")
56
+ # @click.option('--attacks_per_version', required=False, type=int, default=5,
57
+ # help="Number of attacks for each verifypin version.")
58
+ # @click.option('--verbose/--no-verbose', default=False)
59
+ # def stm32_benchmark_traitor_verifypin(samva_results_dir, verifypin_dir, traitor_port, attacked_versions, # stm32_port
60
+ # attacks_per_version, verbose):
61
+ # setup_logging(logging.DEBUG if verbose else -1)
62
+ # targets = prepare_campaign(samva_results_dir, verifypin_dir, attacks_per_version, attacked_versions)
63
+ # # launch_campaign(samva_results_dir, traitor_port, stm32_port, targets)
64
+ # launch_campaign(samva_results_dir, traitor_port, None, targets)
65
+ #
66
+ # @click.command()
67
+ # @click.argument('program_file', required=1, type=str)
68
+ # @click.argument('samva_file', required=1, type=str)
69
+ # def test_riscv(program_file, samva_file):
70
+ # setup_logging(logging.DEBUG)
71
+ # samva = Samva(program_file, config_file=samva_file, fault_model=FaultModel.TrueNOP, strategy="stack_safe")
72
+ # fault_parameters = FaultParameters(2, 10, 2, 3600)
73
+ # attack_result = samva.find_attack_paths(fault_parameters, 100, None)
74
+ # if attack_result.attack_paths is None:
75
+ # log.error("No attack path found.")
76
+ # return
77
+ # binary_file = program_file.replace(".elf", ".bin")
78
+ # with CV32E40P_Litex(samva, "/dev/ttyUSB2", binary_file) as cv32_board:
79
+ # log.warning(f"Run 1 = {cv32_board.run() = }")
80
+ # log.warning(f"Run 2 = {cv32_board.run() = }")
81
+ # log.warning(f"Run 3 = {cv32_board.run() = }")
82
+ #
83
+ # iter_max = min(100, len(attack_result.attack_paths))
84
+ # time_total = 0
85
+ # for i, attack_path in enumerate(attack_result.attack_paths[:iter_max]):
86
+ # samva_lines = convert_attack_path(attack_path)
87
+ # expected_values = []
88
+ # for state in samva.samva_model.attack_options.simulation_final_state:
89
+ # symbol, symbol_size, expected_value = state.symbol, state.size, state.value
90
+ # symbol_addr = samva.samva_model.find_symbol_by_name(symbol).address
91
+ # expected_values.append((symbol_addr, symbol_size, expected_value))
92
+ # time_t0 = time.time()
93
+ # validated = cv32_board.validate_instruction_skip_attack(samva_lines, expected_results=expected_values)
94
+ # time_t1 = time.time()
95
+ # time_total += time_t1 - time_t0
96
+ # log.debug(f"{i = } - {validated = }")
97
+ # log.debug(f"Validated {iter_max} attack paths, in {time_total:.2f} seconds,"
98
+ # f"mean of {time_total/iter_max:.2f} seconds per attack path.")
99
+ #
100
+ #
101
+ # @click.command()
102
+ # @click.argument('samva_results_dir', required=1, type=click.Path(exists=True, file_okay=False, writable=True))
103
+ # @click.argument('verifypin_dir', required=1, type=click.Path(exists=True, file_okay=False))
104
+ # @click.argument('traitor_port', required=1, type=str)
105
+ # # @click.argument('stm32_port', required=1, type=str)
106
+ # @click.argument('version', required=1, type=str)
107
+ # @click.argument('extension', required=1, type=str)
108
+ # @click.argument('attack_number', required=1, type=str)
109
+ # @click.option('--verbose/--no-verbose', default=False)
110
+ # def stm32_traitor_attack_verifypin(samva_results_dir, verifypin_dir, traitor_port, version, extension, # stm32_port
111
+ # attack_number, verbose):
112
+ # setup_logging(logging.INFO if verbose else -1)
113
+ # target = create_target(samva_results_dir, verifypin_dir, version, extension, attack_number)
114
+ # if target is None:
115
+ # log.error("Impossible to create target!")
116
+ # else:
117
+ # launch_campaign(samva_results_dir, traitor_port, None, [target])
118
+ #
119
+ #
120
+ #
121
+ # @click.command()
122
+ # @click.argument('program_file', required=1, type=str)
123
+ # @click.argument('output_file', required=1, type=str)
124
+ # @click.argument('code_addr', required=1, type=BASED_INT)
125
+ # @click.argument('nb_word', required=1, type=int)
126
+ # @click.option('--regs_value', type=(BASED_INT, BASED_INT, BASED_INT, BASED_INT, BASED_INT, BASED_INT,
127
+ # BASED_INT, BASED_INT, BASED_INT, BASED_INT), required=0, default=None)
128
+ # @click.option('--max_repeat', required=False, type=int, default=None)
129
+ # @click.option('--config_file', default=None, type=str)
130
+ # @click.option('--map', default="", type=str)
131
+ # # @click.option('--inst', type=(int, int, int, int, int, int, int, int), required=0, default=None)
132
+ # def simulate(program_file, output_file, code_addr, nb_word, regs_value, max_repeat, config_file, map):
133
+ # parent_dir = os.path.dirname(output_file)
134
+ # if not os.path.exists(parent_dir):
135
+ # log.error(f"Output directory '{parent_dir}' does not exist!")
136
+ # sys.exit(-1)
137
+ #
138
+ # config_text = samva_config_characterization if config_file is None else None
139
+ # samva = Samva(program_file, config_file=config_file, config_text=config_text, fault_model=FaultModel.Replay,
140
+ # strategy="debug")
141
+ #
142
+ # patcher = Patcher(samva.samva_model)
143
+ #
144
+ # filter_regs = ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9']
145
+ # file_offset = patcher.convert_virtual_to_offset(code_addr)
146
+ #
147
+ # with open(program_file, "rb") as file:
148
+ # file.seek(file_offset)
149
+ # nb_byte = nb_word * 4
150
+ # code = file.read(nb_byte)
151
+ #
152
+ # if regs_value is None:
153
+ # registers_value = default_register_value(0)
154
+ # else:
155
+ # registers_value = dict_register_value(*regs_value)
156
+ #
157
+ #
158
+ # simu = SimulationCharacterisation()
159
+ # if map == "ram":
160
+ # simu.simulate_faults_ram(code,
161
+ # registers_value=registers_value,
162
+ # filter_regs=filter_regs,
163
+ # save_file=output_file,
164
+ # max_repetition=max_repeat,
165
+ # extra_sim=1)
166
+ #
167
+ # else:
168
+ # simu.simulate_faults(code,
169
+ # registers_value=registers_value,
170
+ # filter_regs=filter_regs,
171
+ # save_file=output_file,
172
+ # max_repetition=max_repeat,
173
+ # extra_sim=1)
174
+ #
175
+ # @click.command()
176
+ # @click.argument('program_file', required=1, type=str)
177
+ # @click.argument('output_file', required=1, type=str)
178
+ # @click.option('--regs_value', type=(BASED_INT, BASED_INT, BASED_INT, BASED_INT, BASED_INT, BASED_INT,
179
+ # BASED_INT, BASED_INT, BASED_INT, BASED_INT), required=0, default=None)
180
+ # @click.option('--max_repeat', required=False, type=int, default=None)
181
+ # @click.option('--config_file', default=None, type=str)
182
+ # def simulate_branch(program_file, output_file, regs_value, max_repeat, config_file):
183
+ # parent_dir = os.path.dirname(output_file)
184
+ # if not os.path.exists(parent_dir):
185
+ # log.error(f"Output directory '{parent_dir}' does not exist!")
186
+ # sys.exit(-1)
187
+ #
188
+ # config_text = samva_config_characterization if config_file is None else None
189
+ # samva = Samva(program_file, config_file=config_file, config_text=config_text, fault_model=FaultModel.Replay,
190
+ # strategy="debug")
191
+ #
192
+ # simu = SimulationBranchCharacterisation()
193
+ # simu.simulate_faults(samva, regs_value, max_repeat)
194
+ #
195
+ #
196
+ # def characterization_impl(traitor_port, stm32_port, program_file, simulation_file, output_file,
197
+ # amplitudes, delays, durations, repetition, stlink):
198
+ # filter_regs = ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9']
199
+ #
200
+ # with open(simulation_file) as simu_json:
201
+ # simu = json.load(simu_json)
202
+ #
203
+ # if output_file is not None and not os.path.exists(output_file):
204
+ # with open(output_file, "w") as out:
205
+ # out.write("delay,duration,amplitude,test_number,classification,r0,r1,r2,r3,r4,r5,r6,r7,r8,r9\n")
206
+ #
207
+ # params = [(amp, delay, duration, test_number) for amp in range(amplitudes[0], amplitudes[1] - 1, -1)
208
+ # for delay in range(delays[0], delays[1] + 1)
209
+ # for duration in range(durations[0], durations[1] + 1)
210
+ # for test_number in range(repetition)]
211
+ #
212
+ # with Traitor(traitor_port) as traitor, STM32Flash(stm32_port, hla_serial=stlink) as stm32_flash:
213
+ # traitor.clear()
214
+ # stm32_flash.program(program_file)
215
+ #
216
+ # traitor.execute_attack(stm32_flash)
217
+ # regs_normal = stm32_flash.ocd.read_all_registers()
218
+ # log.debug(f"{regs_normal = }")
219
+ # pc_normal = regs_normal["pc"]
220
+ #
221
+ # with logging_redirect_tqdm():
222
+ # for param in tqdm(params):
223
+ # amp, delay, duration, test_number = param
224
+ # faults = [(delay, duration)]
225
+ #
226
+ # try:
227
+ # traitor.execute_attack(stm32_flash, amp, faults)
228
+ #
229
+ # regs2 = stm32_flash.ocd.read_all_registers()
230
+ # pc = regs2["pc"]
231
+ # res = {reg: regs2[reg] for reg in filter_regs}
232
+ #
233
+ # if pc != pc_normal:
234
+ # classification = "crash"
235
+ # else:
236
+ # classification = []
237
+ # for exec_name, regs in simu["simulations"].items():
238
+ # if res == regs:
239
+ # classification.append(exec_name)
240
+ #
241
+ # if len(classification) == 0:
242
+ # classification = "undefined"
243
+ # elif len(classification) == 1:
244
+ # classification = classification[0]
245
+ # else:
246
+ # classification = f"\"{str(classification).replace(',', ';')}\""
247
+ # except KeyboardInterrupt:
248
+ # log.warning("Characterization canceled.")
249
+ # sys.exit(0)
250
+ # except:
251
+ # classification = "crash"
252
+ # res = {reg: -1 for reg in filter_regs}
253
+ # finally:
254
+ # if classification == "undefined":
255
+ # log.warning(f"({delay}, {duration}) / {amp} | {classification} - {res}")
256
+ # else:
257
+ # log.info(f"({delay}, {duration}) / {amp} | {classification} - {res}")
258
+ #
259
+ # if output_file is not None:
260
+ # with open(output_file, "a") as out:
261
+ # out_text = f"{delay},{duration},{amp},{test_number},{classification},"
262
+ # out_text += f"{res['r0']},{res['r1']},{res['r2']},{res['r3']},"
263
+ # out_text += f"{res['r4']},{res['r5']},{res['r6']},{res['r7']}"
264
+ # out_text += f"{res['r8']},{res['r9']}\n"
265
+ # out.write(out_text)
266
+
267
+ #
268
+ # @click.command()
269
+ # @click.argument('test_case_directory', required=1)
270
+ # @click.argument('traitor_port', required=1, type=str)
271
+ # @click.option('--stlink', default=None, type=str)
272
+ # def test_bkpt(test_case_directory, traitor_port, stlink):
273
+ # test_case_name = os.path.basename(os.path.normpath(test_case_directory))
274
+ # log.debug(f"Test case name: {test_case_name}")
275
+ # binary_file = os.path.join(os.path.abspath(test_case_directory), f"bin/{test_case_name}.elf")
276
+ #
277
+ # with Traitor(traitor_port) as traitor, STM32Flash(None, hla_serial=stlink) as stm32_flash:
278
+ # traitor.clear()
279
+ # stm32_flash.program(binary_file)
280
+ #
281
+ # bkpt_p1 = 0x80002b0
282
+ # # bkpt_p2 = 0x8000294
283
+ # bkpt_p2 = 0x800029c
284
+ #
285
+ # stm32_flash.reset()
286
+ # stm32_flash.halt()
287
+ # # stm32_flash.ocd.set_soft_breakpoint(bkpt_p1)
288
+ # # stm32_flash.ocd.set_soft_breakpoint(bkpt_p2)
289
+ #
290
+ # # stm32_flash.reset()
291
+ # # stm32_flash.halt()
292
+ # # stm32_flash.ocd.__send_str("w 0x80002aa 2 r value " + hex(bkpt_p1) + " \n")
293
+ # # stm32_flash.ocd.__read_until(b"> ")
294
+ # # stm32_flash.ocd.set_soft_breakpoint(bkpt_p1)
295
+ #
296
+ # traitor.execute_attack(stm32_flash)
297
+ # regs_normal = stm32_flash.ocd.read_all_registers()
298
+ # pc_normal = regs_normal["pc"]
299
+ # # log.debug(f"{regs_normal = }")
300
+ # log.debug(f"PC before attack 0x{pc_normal:0x}")
301
+ #
302
+ # # for i in range(10):
303
+ # # for j in range(10):
304
+ # # amp, delay, duration = (490, 48, 3)
305
+ # # faults = [(delay, duration)]
306
+ #
307
+ # # for bkpt in range(0x80002a8, 0x8000280, -2):
308
+ # # stm32_flash.reset()
309
+ # # stm32_flash.halt()
310
+ # # stm32_flash.ocd.enable_hard_breakpoint(bkpt)
311
+ #
312
+ # for amp in range(491, 500):
313
+ # for delay in range(35, 55):
314
+ # for duration in range(3, 7):
315
+ # faults = [(delay, duration)]
316
+ #
317
+ # try:
318
+ # traitor.execute_attack(stm32_flash, amp, faults)
319
+ # time.sleep(0.1)
320
+ # regs = stm32_flash.ocd.read_all_registers()
321
+ # # log.debug(f"{regs = }")
322
+ # pc_attack = regs["pc"]
323
+ # # log.debug(f"BKPT = 0x{bkpt:0x} - PC = 0x{pc_attack:0x}")
324
+ # log.debug(f"{amp} - ({delay}, {duration}) PC = 0x{pc_attack:0x}")
325
+ # except KeyboardInterrupt:
326
+ # log.warning("canceled.")
327
+ # sys.exit(0)
328
+ # except:
329
+ # log.warning("crash")
330
+ #
331
+ # # stm32_flash.reset()
332
+ # # stm32_flash.halt()
333
+ # # stm32_flash.ocd.disable_soft_breakpoints()
334
+ #
335
+ #
336
+ # @click.command()
337
+ # @click.argument('traitor_port', required=1, type=str)
338
+ # @click.argument('stm32_port', required=1, type=str)
339
+ # @click.argument('program_file', required=1, type=str)
340
+ # @click.argument('simulation_file', required=1, type=str)
341
+ # @click.argument('output_file', required=1, type=str)
342
+ # @click.option('--amplitudes', type=(int, int), default=(530, 460))
343
+ # @click.option('--delays', type=(int, int), default=(10, 20))
344
+ # @click.option('--durations', type=(int, int), default=(1, 15))
345
+ # @click.option('--repetition', required=False, type=int, default=10)
346
+ # @click.option('--stlink', default=None, type=str)
347
+ # def characterization(traitor_port, stm32_port, program_file, simulation_file, output_file,
348
+ # amplitudes, delays, durations, repetition, stlink):
349
+ # characterization_impl(traitor_port, stm32_port, program_file, simulation_file, output_file,
350
+ # amplitudes, delays, durations, repetition, stlink)
351
+ #
352
+ #
353
+ # @click.command()
354
+ # @click.argument('test_case_directory', required=1)
355
+ # @click.argument('traitor_port', required=1, type=str)
356
+ # @click.option('--amplitudes', type=(int, int), default=(530, 460))
357
+ # @click.option('--delays', type=(int, int), default=(10, 20))
358
+ # @click.option('--durations', type=(int, int), default=(1, 15))
359
+ # @click.option('--repetition', required=False, type=int, default=10)
360
+ # @click.option('--stlink', default=None, type=str)
361
+ # def test_case(test_case_directory, traitor_port, amplitudes, delays, durations, repetition, stlink):
362
+ # if not os.path.exists(test_case_directory):
363
+ # log.error(f"Test case directory '{test_case_directory}' does not exist!")
364
+ # sys.exit(-1)
365
+ #
366
+ # test_case_name = os.path.basename(os.path.normpath(test_case_directory))
367
+ # log.debug(f"Test case name: {test_case_name}")
368
+ #
369
+ # binary_file = os.path.join(os.path.abspath(test_case_directory), f"bin/{test_case_name}.elf")
370
+ # if not os.path.exists(binary_file):
371
+ # log.error(f"Binary file '{binary_file}' does not exist!")
372
+ # sys.exit(-1)
373
+ #
374
+ # simu_file = os.path.join(os.path.abspath(test_case_directory), "simulations.json")
375
+ # if not os.path.exists(simu_file):
376
+ # log.error(f"Simulations file '{simu_file}' does not exist!")
377
+ # sys.exit(-1)
378
+ #
379
+ # timestamp = str(time.strftime("%m%d%H%M%S"))
380
+ # output_file = os.path.join(os.path.abspath(test_case_directory), f"traitor/test_{timestamp}.csv")
381
+ # Path(output_file).parent.mkdir(exist_ok=True, parents=True)
382
+ #
383
+ # characterization_impl(traitor_port, None, binary_file, simu_file, output_file, amplitudes, delays,
384
+ # durations, repetition, stlink)
385
+ #
@@ -0,0 +1,24 @@
1
+ # SAMVA, initial software
2
+ # Copyright © INRIA, Affero GPL v3, 2022, v 1.0
3
+ import logging
4
+ import os
5
+ from functools import partial
6
+
7
+ from samva.commands.commands_common import check_logging, must_be_chained
8
+ from samva.physical.boards.targets.cv32e40p_litex import CV32E40P_Litex
9
+
10
+ log = logging.getLogger(name=f"samva-{__name__}")
11
+ log.setLevel(logging.DEBUG)
12
+
13
+
14
+ @check_logging
15
+ @must_be_chained(False)
16
+ def run_cv32e40p(previous, *, port, verbose):
17
+ for binary_name in previous.keys():
18
+ samva = previous[binary_name]["samva"]
19
+ binary_file = samva.binary_file.replace(".elf", ".bin")
20
+ if not os.path.isfile(binary_file):
21
+ log.error(f"Binary file '{binary_file}' does not exist. It should be placed in the same directory as the .elf file.")
22
+ else:
23
+ previous[binary_name]["create_target"] = partial(CV32E40P_Litex, samva, port, binary_file)
24
+ return previous
@@ -0,0 +1,37 @@
1
+ # SAMVA, initial software
2
+ # Copyright © INRIA, Affero GPL v3, 2022, v 1.0
3
+ import logging
4
+
5
+ from samva.commands.commands_common import check_logging, must_be_chained, hardware_attack
6
+ from samva.physical.analysis.samva_file_line import convert_attack_path
7
+
8
+ log = logging.getLogger(name=f"samva-{__name__}")
9
+ log.setLevel(logging.DEBUG)
10
+
11
+
12
+ @check_logging
13
+ @must_be_chained(True)
14
+ @hardware_attack
15
+ def run_debugger(previous, *, target, binary_name, verbose):
16
+ if "samva" not in previous[binary_name] or "attack_result" not in previous[binary_name]:
17
+ log.error(f"Cannot run debugger validation on {binary_name} if an analysis is not run before.")
18
+ return 0, 0
19
+
20
+ samva = previous[binary_name]["samva"]
21
+ attack_result = previous[binary_name]["attack_result"]
22
+ iter_max = len(attack_result.attack_paths)
23
+ validated_counter = 0
24
+
25
+ with target:
26
+ for i, attack_path in enumerate(attack_result.attack_paths[:iter_max]):
27
+ samva_lines = convert_attack_path(attack_path)
28
+
29
+ initial_values = samva.samva_model.convert_simulation_states(samva.samva_model.attack_options.simulation_initial_state)
30
+ final_values = samva.samva_model.convert_simulation_states(samva.samva_model.attack_options.simulation_final_state)
31
+
32
+ validated = target.validate_instruction_skip_attack(samva_lines, initial_values=initial_values,
33
+ final_values=final_values)
34
+ if validated:
35
+ validated_counter += 1
36
+
37
+ return iter_max, validated_counter
@@ -0,0 +1,186 @@
1
+ # SAMVA, initial software
2
+ # Copyright © INRIA, Affero GPL v3, 2022, v 1.0
3
+ import logging
4
+ import os
5
+ import re
6
+ import time
7
+
8
+ import click
9
+
10
+ from samva.commands.commands_common import check_logging, must_be_chained
11
+ from samva.core import Samva
12
+ from samva.core.common import FaultParameters, FaultModel
13
+ from samva.core.common.analysis_utils import Timing
14
+ from samva.evaluator import EvaluatorSimulationUnicorn2, EvaluatorSimulationGem5
15
+
16
+ log = logging.getLogger(name=f"samva-{__name__}")
17
+ log.setLevel(logging.DEBUG)
18
+
19
+ SAMVA_DEFAULT_RESULT_DIRECTORY = "samva_results"
20
+
21
+
22
+ def prepare_result_folder(result_folder, fault_model, strategy):
23
+ timestamp = str(time.strftime("%Y%m%d_%H%M%S"))
24
+ result_folder = os.path.join(click.format_filename(result_folder),
25
+ "bench_" + fault_model + "_" + strategy + "_" + timestamp + "/")
26
+ os.mkdir(result_folder)
27
+ return result_folder
28
+
29
+ def input_fault_model(fault_model: str) -> FaultModel:
30
+ if fault_model == "TrueNOP":
31
+ return FaultModel.TrueNOP
32
+ elif fault_model == "Replay":
33
+ return FaultModel.Replay
34
+ else:
35
+ return FaultModel.TrueNOP
36
+
37
+ def get_configuration_file_from_binary_name(binary_file: str) -> str:
38
+ f = os.path.splitext(os.path.basename(binary_file))[0]
39
+ conf_file_name = f"{f}.json"
40
+ if "_alig" in binary_file:
41
+ conf_file_name = re.sub(r"_alig\d?", "", conf_file_name)
42
+ pass
43
+ return conf_file_name
44
+
45
+ @check_logging
46
+ @must_be_chained(False)
47
+ def run_evaluate(previous, *, bin_path, samva_config_path, result_folder, fault_model, fault_parameters,
48
+ max_paths_per_segment, max_cost_per_segment, max_cost_for_simulation, max_attack_paths, evaluator,
49
+ unicorn_full_init, log_variable, timeout, stop_at_robustness, deprecated,
50
+ deprecated_exhaustive, deprecated_max_solutions, verbose) -> dict:
51
+
52
+ if not os.path.exists(bin_path):
53
+ raise FileNotFoundError(f"Binary path {bin_path} does not exist.")
54
+ if not os.path.exists(samva_config_path):
55
+ raise FileNotFoundError(f"Samva configuration path {samva_config_path} does not exist.")
56
+
57
+ bin_path_is_file = os.path.isfile(bin_path)
58
+ samva_config_path_is_file = os.path.isfile(samva_config_path)
59
+ if not bin_path_is_file and samva_config_path_is_file:
60
+ raise ValueError("Cannot use a binary folder and a single samva configuration file")
61
+
62
+ strategy = "default" # will be removed.
63
+ timeout = None if timeout <= 0 else timeout
64
+ fake_timeout = 999999
65
+ fault_parameters = FaultParameters(fault_parameters[0], fault_parameters[1], fault_parameters[2], fake_timeout)
66
+ if fault_model == "TrueNOP":
67
+ fault_model = FaultModel.TrueNOP
68
+ elif fault_model == "Replay":
69
+ fault_model = FaultModel.Replay
70
+ else:
71
+ raise ValueError("Provided fault model option is incorrect.")
72
+
73
+ samva_list: list[Samva] = []
74
+ if bin_path_is_file and samva_config_path_is_file:
75
+ samva_list.append(
76
+ Samva(
77
+ bin_path,
78
+ config_file=samva_config_path,
79
+ fault_model=fault_model,
80
+ strategy=strategy,
81
+ exhaustive=deprecated_exhaustive,
82
+ )
83
+ )
84
+ elif bin_path_is_file and not samva_config_path_is_file:
85
+ conf_file_name = get_configuration_file_from_binary_name(bin_path)
86
+ suggested_samva_config_path = os.path.abspath(os.path.join(samva_config_path, conf_file_name))
87
+ if not os.path.isfile(suggested_samva_config_path):
88
+ raise FileNotFoundError(f"Attack file does not exist: {suggested_samva_config_path}")
89
+ samva_list.append(
90
+ Samva(
91
+ bin_path,
92
+ config_file=suggested_samva_config_path,
93
+ fault_model=fault_model,
94
+ strategy=strategy,
95
+ exhaustive=deprecated_exhaustive,
96
+ )
97
+ )
98
+ else:
99
+ found_pairs = []
100
+ for f in os.listdir(bin_path):
101
+ if not f.endswith(".elf"):
102
+ continue
103
+ conf_file_name = get_configuration_file_from_binary_name(f)
104
+ suggested_samva_config_path = os.path.abspath(os.path.join(samva_config_path, conf_file_name))
105
+ if not os.path.isfile(suggested_samva_config_path):
106
+ continue
107
+ log.info(f"Found the binary {f} and its corresponding configuration file {suggested_samva_config_path}.")
108
+ found_pairs.append((os.path.join(bin_path, f), suggested_samva_config_path))
109
+ for bin_file, conf_file in found_pairs:
110
+ samva_list.append(
111
+ Samva(
112
+ bin_file,
113
+ config_file=conf_file,
114
+ fault_model=fault_model,
115
+ strategy=strategy,
116
+ exhaustive=deprecated_exhaustive,
117
+ )
118
+ )
119
+ if len(found_pairs) == 0:
120
+ log.info("Unable to found pairs elf file / samva configuration file.")
121
+
122
+ if result_folder is None:
123
+ if not os.path.isdir(SAMVA_DEFAULT_RESULT_DIRECTORY):
124
+ os.mkdir(SAMVA_DEFAULT_RESULT_DIRECTORY)
125
+ result_folder = prepare_result_folder(SAMVA_DEFAULT_RESULT_DIRECTORY, fault_model.value, strategy)
126
+
127
+ match evaluator:
128
+ case "unicorn":
129
+ samva_evaluator = EvaluatorSimulationUnicorn2(
130
+ fault_model=fault_model,
131
+ result_folder=result_folder,
132
+ enable_log=True,
133
+ write_nop_files=True,
134
+ log_variables=log_variable,
135
+ disable_full_init=not unicorn_full_init,
136
+ )
137
+ case "gem5":
138
+ samva_evaluator = EvaluatorSimulationGem5(
139
+ result_folder=result_folder, enable_log=True, write_nop_files=True, log_variables=log_variable
140
+ )
141
+ case _:
142
+ samva_evaluator = None
143
+
144
+ result = {}
145
+ for samva in samva_list:
146
+ with Timing() as t_analysis:
147
+ if deprecated:
148
+ attack_result = samva.find_attack_paths(fault_parameters, deprecated_max_solutions, samva_evaluator)
149
+ else:
150
+ attack_result = samva.find_data_aware_attack_paths(
151
+ fault_parameters,
152
+ samva_evaluator,
153
+ max_paths_per_segment=max_paths_per_segment,
154
+ max_cost_per_segment=max_cost_per_segment,
155
+ max_cost_for_simulation=max_cost_for_simulation,
156
+ max_attack_paths=max_attack_paths,
157
+ stop_when_robustness_found=stop_at_robustness,
158
+ timeout=timeout,
159
+ )
160
+ if max_attack_paths is not None:
161
+ attack_result.attack_paths = attack_result.attack_paths[:max_attack_paths]
162
+
163
+ txt = []
164
+ binary_name = samva.samva_model.binary_name
165
+ if attack_result.attack_paths is None:
166
+ txt.append(f"{binary_name} - Specified fault parameters are not suited for this binary.")
167
+ elif len(attack_result.attack_paths) == 0:
168
+ txt.append(f"{binary_name} - No attack path found.")
169
+ else:
170
+ txt.append(f"{binary_name} - {len(attack_result.attack_paths)} attack paths found.")
171
+ if attack_result.robustness is not None:
172
+ txt.append(" " * len(binary_name) + f" Robustness level is {attack_result.robustness}.")
173
+ txt.append(" " * len(binary_name) + f" Analysis concluded in {t_analysis}")
174
+ if verbose and not deprecated:
175
+ txt.append(" " * len(binary_name) + f" Candidate stats: {samva.cstats}")
176
+ txt.append(" " * len(binary_name) + f" Evaluation stats: {samva.estats}")
177
+
178
+ for lign in txt:
179
+ log.info(lign)
180
+
181
+ result[binary_name] = {}
182
+ result[binary_name]["samva"] = samva
183
+ result[binary_name]["attack_result"] = attack_result
184
+ result[binary_name]["log"] = txt
185
+
186
+ return result
@@ -0,0 +1,43 @@
1
+ # SAMVA, initial software
2
+ # Copyright © INRIA, Affero GPL v3, 2022, v 1.0
3
+ import logging
4
+
5
+ from samva.commands.commands_common import check_logging, must_be_chained
6
+ from samva.core.common import AttackPointTarget
7
+
8
+ log = logging.getLogger(name=f"samva-{__name__}")
9
+ log.setLevel(logging.DEBUG)
10
+
11
+
12
+ @check_logging
13
+ @must_be_chained(True)
14
+ def run_measure_cycles(previous, *, addr1 , addr2, verbose):
15
+ log.info(f"Measure cycles between {addr1} and {addr2}")
16
+
17
+ for binary_name in previous.keys():
18
+ if "create_target" not in previous[binary_name]:
19
+ log.warning(f"Cannot find target for binary {binary_name}")
20
+ continue
21
+
22
+ if "samva" not in previous[binary_name]:
23
+ log.warning(f"Cannot find samva for binary {binary_name}")
24
+ continue
25
+
26
+ samva = previous[binary_name]["samva"]
27
+ addr1 = samva.samva_model.retrieve_address_from_string(addr1, AttackPointTarget.INSTR)
28
+ addr2 = samva.samva_model.retrieve_address_from_string(addr2, AttackPointTarget.INSTR)
29
+
30
+ create_target = previous[binary_name]["create_target"]
31
+ target = create_target()
32
+
33
+ with target:
34
+ measures = target.measure_cycles(addr1, addr2, must_reset=True)
35
+
36
+ if measures is not None:
37
+ sum_cycles = sum(i[1] for i in measures)
38
+ log.info(f"Measured number of cycles: {sum_cycles}")
39
+ previous[binary_name]["measure_cycle"] = sum_cycles
40
+ else:
41
+ log.warning(f"Cannot measure cycles for binary {binary_name}")
42
+
43
+ return previous